Lorenz curve & Gini coefficient
a Gini coefficient of 0.56 is quite high, which means that only a top few producers are responsible for a large amount of the total SUP waste contribution, in other words 10% are responsible for 44% of the waste
name = c('')
df <- plastic %>% select(-polymer_producer, - total_waste_div_production) %>% mutate(rank = -rank) %>% # change sign of rank to make it increase with the dependent variables
rename( assets = no_of_assets,
product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste,
total = total_contribution_to_sup_waste)
library(corrplot) # correlation plots
# https://cran.r-project.org/web/packages/corrplot/vignettes/corrplot-intro.html
cor <- cor(df)
cor_mtest <- cor.mtest(df, conf.level = 0.99) # combining correlogram with significance test
corrplot(cor, method = "number", order = 'hclust', addrect = 3, p.mat = cor_mtest$p, insig = "pch") # order = AOE, FPC, hclust + addrect

corrplot(cor, p.mat = cor_mtest$p, low = cor_mtest$lowCI, upp = cor_mtest$uppCI, order = 'hclust', sig.level = 0.01, tl.pos = 'd', addrect = 3, rect.col = 'navy', plotC = 'rect', cl.pos = 'n', insig = "p-value")

name = c('total_contribution_to_sup_waste', 'Producers')
df <- plastic %>% rename(value = total_contribution_to_sup_waste) %>% select(value)
library(gglorenz) #transformations for plotting Lorenz curve, https://github.com/jjchern/gglorenz
lorenzcurve <- df %>%
ggplot(aes(value)) +
stat_lorenz(desc = FALSE) +
coord_fixed() +
geom_abline(linetype = 'dashed') +
theme_minimal() +
hrbrthemes::scale_x_percent() +
hrbrthemes::scale_y_percent() +
# hrbrthemes::theme_ipsum_rc() +
annotate_ineq(df$value) +
ggtitle(paste("Lorenz curve for", name[1], sep=" "))
lorenzcurve <- ggplotly(lorenzcurve) %>% layout(yaxis = list(title = paste("cumulative percentage of", name[1], sep=" ")), xaxis = list(title = paste("cumulative percentage of", name[2], sep=" ")))
lorenzcurve
| principal component analysis colored by self organizing map cluster |
I need more knowledge how to work with and interpret SOM and PCA, maybe also not enough observations in data set https://iamciera.github.io/SOMexample/html/SOM_RNAseq_tutorial_part2a_SOM.html
but the clustering from SOM, when only independent variables are used, actually show the two assumed subpopulations based on rigid vs flexible produciton
name = c('flexible_format_contribution_to_sup_waste', 'rigid_format_contribution_to_sup_waste', 'Producers', 'flexible', 'rigid')
df <- plastic %>% rename(flexible = flexible_format_contribution_to_sup_waste, rigid = rigid_format_contribution_to_sup_waste) %>% select(flexible, rigid) %>% pivot_longer(cols = c(flexible,rigid))
library(gglorenz) #transformations for plotting Lorenz curve, https://github.com/jjchern/gglorenz
# get ggplot standard colors for grouping, which are equally spaced hues around the color wheel, starting from 15
gg_color_hue <- function(n) {
hues = seq(15, 375, length = n + 1)
hcl(h = hues, l = 65, c = 100)[1:n]
}
lorenzcurve <- df %>%
ggplot(aes(x = value, color = name)) +
stat_lorenz(desc = FALSE) +
coord_fixed() +
geom_abline(linetype = 'dashed') +
theme_minimal() +
hrbrthemes::scale_x_percent() +
hrbrthemes::scale_y_percent() +
# hrbrthemes::theme_ipsum_rc() +
annotate_ineq(filter(df, name == name[4])$value, y = 0.95, colour = gg_color_hue(2)[1]) +
annotate_ineq(filter(df, name == name[5])$value, y = 0.90, colour = gg_color_hue(2)[2]) +
ggtitle(paste("compare Lorenz curve of", name[1], "and", name[2], sep=" "))
lorenzcurve <- ggplotly(lorenzcurve) %>% layout(yaxis = list(title = paste("cumulative percentage of<br>", name[1], "<br>", name[2], sep="")), xaxis = list(title = paste("cumulative percentage of", name[2], sep=" ")))
lorenzcurve
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
rename( product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste)
# We need to normalize the data based on scale function because the variables are different scales; Normalization means subtracting mean from each observation and dividing with standard deviation. Check all the variables mean values are zero now
# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale
head(scale_data)
product flexible rigid
[1,] 11.2 4.7 1.2
[2,] 9.3 4.7 0.9
[3,] 11.6 4.0 1.3
[4,] 5.1 0.2 4.5
[5,] 9.5 3.2 1.1
[6,] 8.8 3.3 0.8
library(kohonen) # functions to train self-organising maps (SOMs)
# principle component analysis
pca <- prcomp(scale_data, scale=FALSE)
summary(pca)
Importance of components:
PC1 PC2 PC3
Standard deviation 2.6049 0.56785 0.17091
Proportion of Variance 0.9507 0.04518 0.00409
Cumulative Proportion 0.9507 0.99591 1.00000
# visualize pcs results
# Contributions of variables to PC1
fviz_contrib(pca, choice = "var", axes = 1, top = 10)

# Contributions of variables to PC2
fviz_contrib(pca, choice = "var", axes = 2, top = 10)

# Control variable colors using their contributions to the principle axis
fviz_pca_var(pca, col.var="contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE # Avoid text overlapping
) + theme_minimal() + ggtitle("Variables - PCA")

# add back to original so everything is together
pca_scores <- data.frame(pca$x)
data_val <- cbind(df, pca_scores)
pca_plot <- ggplot(data_val, aes(x = PC1, y = PC2)) +
geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
geom_density_2d(alpha = 0.2, bins = 4) +# 2D kernel density estimation using MASS::kde2d() and display the results with contours
geom_point(alpha = 0.75) + # point geom is used to create scatterplots
theme_minimal()
pca_plot <- ggplotly(pca_plot) %>% layout()
pca_plot
# clustering is performed using the som() function on the scaled gene expression values.
set.seed(3)
# define a grid for the SOM and train
grid_size <- ncol(scale_data)
som_grid <- somgrid(xdim = grid_size, ydim = grid_size, topo = 'hexagonal')
som_model <- som(scale_data, grid = som_grid)
summary(som_model)
SOM of size 3x3 with a hexagonal topology and a bubble neighbourhood function.
The number of data layers is 1.
Distance measure(s) used: sumofsquares.
Training data included: 100 objects.
Mean distance to the closest unit in the map: 0.244.
# generate som plots after training
plot(som_model, type = 'mapping')

plot(som_model, type = 'codes')

# plot(som_model, type = 'counts')
# plot(som_model, type = 'dist.neighbours')
# plot(som_model, type = 'quality')
# plot(som_model, type = 'changes')
# further split the clusters into a smaller set of clusters using hierarchical clustering.
som_cluster <- cutree(hclust(dist(som_model$codes[[1]])), 2) # use hierarchical clustering to cluster the codebook vectors
plot(som_model, type="mapping", bgcol = som_cluster, main = "Clusters")
add.cluster.boundaries(som_model, som_cluster)

# attach the hierchal cluster to the larger dataset data_val.
gridSquare <- grid_size * grid_size
som_clusterKey <- data.frame(som_cluster)
som_clusterKey$unit_classif <- c(1:gridSquare)
data_val <- cbind(data_val,som_model$unit.classif,som_model$distances) %>% rename(unit_classif = 'som_model$unit.classif', distances = 'som_model$distances')
data_val <- merge(data_val, som_clusterKey, by.x = "unit_classif" )
head(data_val)
# plot pca with colored clusters
pcasom_plot <- ggplot(data_val, aes(x = PC1, y = PC2, color = factor(som_cluster), text = polymer_producer)) +
geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
geom_point(alpha = 0.75) + # point geom is used to create scatterplots
theme_minimal()
pcasom_plot <- ggplotly(pcasom_plot) %>% layout()
pcasom_plot
parallel coordinate plot is not very insightful, since there is so much data close together, we cannot see any grouping without prior knowledge, only hint is when you zoom in at the bulk of the data in the lower third (y direction), there is a negative correlation
# two variables, continuous x, continuous y, show trend and distribution
name = c('production_of_in_scope_polymers', 'total_contribution_to_sup_waste')
df <- merge(plastic, data_val, by.x = 'polymer_producer')
df <- df %>% rename(x = production_of_in_scope_polymers, y = total_contribution_to_sup_waste, cluster = som_cluster, text = polymer_producer) %>% select(x, y, cluster, text)
# https://ggplot2.tidyverse.org/reference/geom_smooth.html
point_plot <- df %>%
ggplot(aes(x = x, y = y, color = factor(cluster))) +
# geom_jitter(alpha = 0.5, size = 1) +
geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
geom_density_2d(alpha = 0.2, bins = 4) +# 2D kernel density estimation using MASS::kde2d() and display the results with contours
geom_smooth(fill = "grey90") + # aids the eye in seeing patterns in the presence of overplotting
geom_point(aes(text = text), alpha = 0.75) + # point geom is used to create scatterplots
theme_minimal() +
ggtitle(paste("trend of", name[2], "over", name[1], sep=" "))
Ignoring unknown aesthetics: text
point_plot <- ggplotly(point_plot) %>% layout(xaxis = list(showticklabels = FALSE))
stat_contour(): Zero contours were generatedkein nicht-fehlendes Argument f昼㹣r min; gebe Inf zur昼㹣ckkein nicht-fehlendes Argument f昼㹣r max; gebe -Inf zur昼㹣ck`geom_smooth()` using method = 'loess' and formula 'y ~ x'
pseudoinverse used at 8.786neighborhood radius 0.714reciprocal condition number 0There are other near singularities as well. 0.25pseudoinverse used at 8.786neighborhood radius 0.714reciprocal condition number 0There are other near singularities as well. 0.25
x_density_plot <- df %>%
ggplot(aes(x = x, color = factor(cluster))) +
stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
# geom_histogram(binwidth = 1) +
theme_minimal()
x_density_plot <- ggplotly(x_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))
y_density_plot <- df %>%
ggplot(aes(x = y, color = factor(cluster))) +
stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
# geom_histogram(binwidth = 1) +
coord_flip() +
theme_minimal()
y_density_plot <- ggplotly(y_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))
# https://ggplot2.tidyverse.org/reference/geom_quantile.html
qualtile_plot <- df %>%
ggplot(aes(x = x, y = y, color = factor(cluster))) +
geom_quantile(alpha = 0.8) + # fits a quantile regression to the data and draws the fitted quantiles with lines
theme_minimal()
qualtile_plot <- ggplotly(qualtile_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE))
Smoothing formula not specified. Using: y ~ x
Smoothing formula not specified. Using: y ~ x
# merge figures into one plot, via subplots, https://plotly-r.com/arranging-views.html
sub1 <- subplot(x_density_plot, plotly_empty(), point_plot, y_density_plot, nrows = 2, margin = 0, heights = c(0.15, 0.85), widths = c(0.9, 0.1), shareX = TRUE, shareY = TRUE, titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
sub2 <- subplot(qualtile_plot, plotly_empty(), margin = 0, widths = c(0.9, 0.10), titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
fig <- subplot(sub1, sub2, nrows = 2, margin = 0, heights = c(0.8, 0.2), shareX = TRUE) %>% layout(xaxis = list(title = name[1]), yaxis = list(title = name[2]))
Can only have one: config
fig
https://statsandr.com/blog/clustering-analysis-k-means-and-hierarchical-clustering-by-hand-and-in-r/#optimal-number-of-clusters As a reminder, this method aims at partitioning n observations into k clusters in which each observation belongs to the cluster with the closest average, serving as a prototype of the cluster works not quite as well as SOM, but very close, also recommend cluster is three, but closely followed by 2, since 3 does not reveal any significant connection right now, I wonder what that shows
name = c('plastic producers clustered by focus')
df <- merge(plastic, select(data_val, som_cluster, polymer_producer), by.x = 'polymer_producer') %>%
mutate(som_cluster = as.character(som_cluster)) %>%
rename(product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste,
assets = no_of_assets,
cluster = som_cluster) %>%
select(assets, product, flexible, rigid, cluster)
library(GGally) # extends ggplot2 by adding several functions to reduce the complexity of combining geoms with transformed data
# https://www.r-graph-gallery.com/parallel-plot-ggally.html#custom
parcoord_plot <- ggparcoord(df,
columns = 1:4, groupColumn = 5,
scale='center', # scaling: standardize and center variables
showPoints = TRUE,
title = name,
alphaLines = 0.3) +
theme_minimal()
parcoord_plot <- ggplotly(parcoord_plot) %>% layout(autosize=T)
parcoord_plot
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
rename( product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste)
library(NbClust) # determining the optimal number of clusters in a data set
library(cluster) # computes agglomerative hierarchical clustering of the dataset
# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale
head(scale_data)
product flexible rigid
[1,] 11.2 4.7 1.2
[2,] 9.3 4.7 0.9
[3,] 11.6 4.0 1.3
[4,] 5.1 0.2 4.5
[5,] 9.5 3.2 1.1
[6,] 8.8 3.3 0.8
kmeans_model <- kmeans(scale_data, centers = 2, nstart = 12) # k-means clustering is done via the kmeans() function, with the argument centers that corresponds to the number of desired clusters
df_cluster <- tibble(df, cluster = as.factor(kmeans_model$cluster)) # store cluster in original data set as column
head(df_cluster)
# check quality of a k-means partition
quality <- kmeans_model$betweenss / kmeans_model$totss
print(paste("quality of kmeans is BSS/TSS: ", format(round(quality,2), nsmall = 2)))
[1] "quality of kmeans is BSS/TSS: 0.74"
# find optimal number of clusters
fviz_nbclust(scale_data, kmeans, method = 'wss') + # Elbow method, needs scaled data
# geom_vline(xintercept = 2, linetype = 2) + # add line for better visualization
labs(subtitle = "Elbow method") # add subtitle

fviz_nbclust(scale_data, kmeans, method = 'silhouette') + # Silhouette method, needs scaled data
labs(subtitle = "Silhouette method") # add subtitle

fviz_nbclust(df[, !names(df) %in% name], kmeans, # Gap statistics, needs original data ?
nstart = 25,
method = 'gap_stat',
nboot = 100) + # reduce it for lower computation time, but less precise results
labs(subtitle = "Gap statistics method")
Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 100) [one "." per sample]:
.................................................. 50
.................................................. 100

nbclust_out <- NbClust(data = df[, !names(df) %in% name], # NbClust needs the original data ?
distance = 'euclidean',
min.nc = 2, # minimum number of clusters
max.nc = 10, # maximum number of cluster
method = 'complete',
index = 'all')
NaNs wurden erzeugt
[1] "Frey index : No clustering structure in this data set"
*** : The Hubert index is a graphical method of determining the number of clusters.
In the plot of Hubert index, we seek a significant knee that corresponds to a
significant increase of the value of the measure i.e the significant peak in Hubert
index second differences plot.

*** : The D index is a graphical method of determining the number of clusters.
In the plot of D index, we seek a significant knee (the significant peak in Dindex
second differences plot) that corresponds to a significant increase of the value of
the measure.
*******************************************************************
* Among all indices:
* 7 proposed 2 as the best number of clusters
* 9 proposed 3 as the best number of clusters
* 1 proposed 5 as the best number of clusters
* 2 proposed 6 as the best number of clusters
* 2 proposed 9 as the best number of clusters
* 2 proposed 10 as the best number of clusters
***** Conclusion *****
* According to the majority rule, the best number of clusters is 3
*******************************************************************

fviz_nbclust(nbclust_out) + theme_minimal() +
labs(subtitle = "NbClust results")
Bedingung hat L攼㸴nge > 1 und nur das erste Element wird benutztBedingung hat L攼㸴nge > 1 und nur das erste Element wird benutztBedingung hat L攼㸴nge > 1 und nur das erste Element wird benutztBedingung hat L攼㸴nge > 1 und nur das erste Element wird benutzt
Among all indices:
===================
* 2 proposed 0 as the best number of clusters
* 7 proposed 2 as the best number of clusters
* 9 proposed 3 as the best number of clusters
* 1 proposed 5 as the best number of clusters
* 2 proposed 6 as the best number of clusters
* 2 proposed 9 as the best number of clusters
* 2 proposed 10 as the best number of clusters
* 1 proposed NA's as the best number of clusters
Conclusion
=========================
* According to the majority rule, the best number of clusters is 3 .

# check quality of clustering
# if a large majority of the silhouette coefficients are positive, it indicates that the observations are placed in the correct group
sil <- silhouette(kmeans_model$cluster, dist(scale_data))
fviz_silhouette(sil)

fviz_cluster(kmeans_model, df[, !names(df) %in% name], ellipse.type = 'norm') + theme_minimal()

fviz_cluster(kmeans_model, df[, !names(df) %in% name]) + theme_minimal()

name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
rename( product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste)
# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale
kmean_calc <- function(df, ...){
kmeans(df, scaled = ..., nstart = 30)
}
km2 <- kmean_calc(scale_data, 2)
km3 <- kmean_calc(scale_data, 3)
km4 <- kmeans(scale_data, 4)
km5 <- kmeans(scale_data, 5)
km6 <- kmeans(scale_data, 6)
km7 <- kmeans(scale_data, 7)
p1 <- fviz_cluster(km2, data = scale_data, ellipse.type = "convex") + theme_minimal()
p1 <- ggplotly(p1) %>% layout(annotations = list(text = "k = 2", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p2 <- fviz_cluster(km3, data = scale_data, ellipse.type = "convex") + theme_minimal()
p2 <- ggplotly(p2) %>% layout(annotations = list(text = "k = 3", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p3 <- fviz_cluster(km4, data = scale_data, ellipse.type = "convex") + theme_minimal()
p3 <- ggplotly(p3) %>% layout(annotations = list(text = "k = 4", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p4 <- fviz_cluster(km5, data = scale_data, ellipse.type = "convex") + theme_minimal()
p4 <- ggplotly(p4) %>% layout(annotations = list(text = "k = 5", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p5 <- fviz_cluster(km6, data = scale_data, ellipse.type = "convex") + theme_minimal()
p5 <- ggplotly(p5) %>% layout(annotations = list(text = "k = 6", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p6 <- fviz_cluster(km7, data = scale_data, ellipse.type = "convex") + theme_minimal()
p6 <- ggplotly(p6) %>% layout(annotations = list(text = "k = 7", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
fig <- subplot(p1, p2, p3 , p4, p5, p6, nrows = 2, shareX = TRUE, shareY = TRUE) %>% layout() # TOOD: make all plots linked
fig
https://statsandr.com/blog/clustering-analysis-k-means-and-hierarchical-clustering-by-hand-and-in-r/#optimal-number-of-clusters remind that the difference with the partition by k-means is that for hierarchical clustering, the number of classes is not specified in advance
it seems that most clusters are confused by some specific values (81, 63, 72, 36?), are they some sort of outlier, or what is special about them?
# two variables, continuous x, continuous y, show trend and distribution
name = c('production_of_in_scope_polymers', 'total_contribution_to_sup_waste')
df <- tibble(plastic, cluster = as.factor(kmeans_model$cluster))
df <- df %>% rename(x = production_of_in_scope_polymers, y = total_contribution_to_sup_waste, cluster = cluster, text = polymer_producer) %>% select(x, y, cluster, text)
# https://ggplot2.tidyverse.org/reference/geom_smooth.html
point_plot <- df %>%
ggplot(aes(x = x, y = y, color = factor(cluster))) +
# geom_jitter(alpha = 0.5, size = 1) +
geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
geom_density_2d(alpha = 0.2, bins = 4) +# 2D kernel density estimation using MASS::kde2d() and display the results with contours
geom_smooth(fill = "grey90") + # aids the eye in seeing patterns in the presence of overplotting
geom_point(aes(text = text), alpha = 0.75) + # point geom is used to create scatterplots
theme_minimal() +
ggtitle(paste("trend of", name[2], "over", name[1], sep=" "))
Ignoring unknown aesthetics: text
point_plot <- ggplotly(point_plot) %>% layout(xaxis = list(showticklabels = FALSE))
stat_contour(): Zero contours were generatedkein nicht-fehlendes Argument f昼㹣r min; gebe Inf zur昼㹣ckkein nicht-fehlendes Argument f昼㹣r max; gebe -Inf zur昼㹣ck`geom_smooth()` using method = 'loess' and formula 'y ~ x'
x_density_plot <- df %>%
ggplot(aes(x = x, color = factor(cluster))) +
stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
# geom_histogram(binwidth = 1) +
theme_minimal()
x_density_plot <- ggplotly(x_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))
y_density_plot <- df %>%
ggplot(aes(x = y, color = factor(cluster))) +
stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
# geom_histogram(binwidth = 1) +
coord_flip() +
theme_minimal()
y_density_plot <- ggplotly(y_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))
# https://ggplot2.tidyverse.org/reference/geom_quantile.html
qualtile_plot <- df %>%
ggplot(aes(x = x, y = y, color = factor(cluster))) +
geom_quantile(alpha = 0.8) + # fits a quantile regression to the data and draws the fitted quantiles with lines
theme_minimal()
qualtile_plot <- ggplotly(qualtile_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE))
Smoothing formula not specified. Using: y ~ x
Smoothing formula not specified. Using: y ~ x
# merge figures into one plot, via subplots, https://plotly-r.com/arranging-views.html
sub1 <- subplot(x_density_plot, plotly_empty(), point_plot, y_density_plot, nrows = 2, margin = 0, heights = c(0.15, 0.85), widths = c(0.9, 0.1), shareX = TRUE, shareY = TRUE, titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
sub2 <- subplot(qualtile_plot, plotly_empty(), margin = 0, widths = c(0.9, 0.10), titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
Based on info supplied, a 'scatter' trace seems appropriate.
Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
Setting the mode to markers
Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
fig <- subplot(sub1, sub2, nrows = 2, margin = 0, heights = c(0.8, 0.2), shareX = TRUE) %>% layout(xaxis = list(title = name[1]), yaxis = list(title = name[2]))
Can only have one: config
fig
| https://towardsdatascience.com/10-tips-for-choosing-the-optimal-number-of-clusters-277e93d72d92 In this figure the size of each node corresponds to the number of samples in each cluster, and the arrows are coloured according to the number of samples each cluster receives. A separate set of arrows, the transparent ones, called the incoming node proportion, are also coloured and shows how samples from one group end up in another group — an indicator of cluster instability. |
|
|
|
| ```r name = c(‘polymer_producer’) df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other rename( product = production_of_in_scope_polymers, flexible = flexible_format_contribution_to_sup_waste, rigid = rigid_format_contribution_to_sup_waste) |
| # scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name])))) # scale_data <- as.matrix(scale(df[, !names(df) %in% name])) scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale head(scale_data) ``` |
|
|
product flexible rigid [1,] 11.2 4.7 1.2 [2,] 9.3 4.7 0.9 [3,] 11.6 4.0 1.3 [4,] 5.1 0.2 4.5 [5,] 9.5 3.2 1.1 [6,] 8.8 3.3 0.8 |
|
|
| ```r no_k = 2; |
| # Hierarchical clustering: single linkage hclust_res <- hclust(dist(scale_data), method = ‘single’) fviz_dend(hclust_res, k = no_k, rect = TRUE) ``` |
|
|
`guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.row names were found from a short variable and have been discarded |
|
|
 |
|
|
r # Hierarchical clustering: complete linkage hclust_res <- hclust(dist(scale_data), method = 'complete') plot(hclust_res) rect.hclust(hclust_res, k = no_k, border = 'blue') |
|
|
 |
|
|
| ```r |
| # Hierarchical clustering: average linkage hclust_res <- hclust(dist(scale_data), method = ‘average’) plot(hclust_res) rect.hclust(hclust_res, k = no_k, border = ‘blue’) ``` |
|
|
 |
|
|
| ```r |
| # Hierarchical clustering: ward hclust_res <- hclust(dist(scale_data), method = ‘ward.D2’) fviz_dend(hclust_res, k = no_k, rect = TRUE) ``` |
|
|
`guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.row names were found from a short variable and have been discarded |
|
|
 |
|
|
r # Hierarchical clustering: mcquitty hclust_res <- hclust(dist(scale_data), method = 'mcquitty') plot(hclust_res) rect.hclust(hclust_res, k = no_k, border = 'blue') |
|
|
 |
|
|
| ```r |
| # Hierarchical clustering: centroid hclust_res <- hclust(dist(scale_data), method = ‘centroid’) plot(hclust_res) rect.hclust(hclust_res, k = no_k, border = ‘blue’) ``` |
|
|
 |
|
|
|
clValid to choose best clustering algo
https://towardsdatascience.com/10-tips-for-choosing-the-optimal-number-of-clusters-277e93d72d92 The cValid package can be used to simultaneously compare multiple clustering algorithms, to identify the best clustering approach and the optimal number of clusters. We will compare k-means, hierarchical and PAM clustering. Connectivity and Silhouette are both measurements of connectedness while the Dunn Index is the ratio of the smallest distance between observations not in the same cluster to the largest intra-cluster distance.
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
rename( product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste)
# config
no_of_cluster = 9
library(clustree) # produce clustering trees, a visualization for interrogating clusterings as resolution increases
Lade n昼㸶tiges Paket: ggraph
# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale
head(scale_data)
product flexible rigid
[1,] 11.2 4.7 1.2
[2,] 9.3 4.7 0.9
[3,] 11.6 4.0 1.3
[4,] 5.1 0.2 4.5
[5,] 9.5 3.2 1.1
[6,] 8.8 3.3 0.8
tmp <- NULL
for (k in 1:no_of_cluster){
tmp[k] <- kmeans(scale_data, k, nstart = 30)
}
Anzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴nge
tmp_df <- data.frame(tmp)
colnames(tmp_df) <- seq(1:no_of_cluster) # add prefix to the column names
colnames(tmp_df) <- paste0("k", colnames(tmp_df))
# get individual PCA
tmp_df.pca <- prcomp(tmp_df, center = TRUE, scale. = FALSE)
ind.coord <- tmp_df.pca$x
ind.coord <- ind.coord[,1:2]
tmp_df <- bind_cols(as.data.frame(tmp_df), as.data.frame(ind.coord))
clustree(tmp_df, prefix = "k") # produce clustering trees, a visualization for interrogating clustering as resolution increases
The `add` argument of `group_by()` is deprecated as of dplyr 1.0.0.
Please use the `.add` argument instead.

overlay_list <- clustree_overlay(tmp_df, prefix = "k", x_value = "PC1", y_value = "PC2", plot_sides = TRUE)
overlay_list$overlay

overlay_list$x_side

overlay_list$y_side

| Extracting Features of Clusters |
https://towardsdatascience.com/10-tips-for-choosing-the-optimal-number-of-clusters-277e93d72d92 Ultimately, we would like to answer questions like “what is it that makes this cluster unique from others?” and “what are the clusters that are similar to one another”. Let’s select four clusters and interrogate the features of these clusters.
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
rename( product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste)
library(clValid) # Statistical and biological validation of clustering results. This package implements Dunn Index, Silhouette, Connectivity, Stability, BHI and BSI
library(mclust) # BIC for parameterized Gaussian mixture models fitted by EM algorithm initialized by model-based hierarchical clustering
__ ___________ __ _____________
/ |/ / ____/ / / / / / ___/_ __/
/ /|_/ / / / / / / / /\__ \ / /
/ / / / /___/ /___/ /_/ /___/ // /
/_/ /_/\____/_____/\____//____//_/ version 5.4.7
Type 'citation("mclust")' for citing this R package in publications.
Attache Paket: 㤼㸱mclust㤼㸲
The following object is masked from 㤼㸱package:kohonen㤼㸲:
map
The following object is masked from 㤼㸱package:purrr㤼㸲:
map
# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale
intern <- clValid(scale_data, nClust = 2:9,
clMethods = c("hierarchical", "kmeans", "diana", "fanny", "som", "model", "sota", "pam", "clara", "agnes"),
validation = "internal")
Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...
fitting ...
|
| | 0%
|
|=========== | 7%
|
|====================== | 13%
|
|================================= | 20%
|
|============================================= | 27%
|
|======================================================== | 33%
|
|=================================================================== | 40%
|
|============================================================================== | 47%
|
|========================================================================================= | 53%
|
|==================================================================================================== | 60%
|
|=============================================================================================================== | 67%
|
|========================================================================================================================== | 73%
|
|====================================================================================================================================== | 80%
|
|================================================================================================================================================= | 87%
|
|============================================================================================================================================================ | 93%
|
|=======================================================================================================================================================================| 100%
fitting ...
|
| | 0%
|
|=========== | 7%
|
|====================== | 13%
|
|================================= | 20%
|
|============================================= | 27%
|
|======================================================== | 33%
|
|=================================================================== | 40%
|
|============================================================================== | 47%
|
|========================================================================================= | 53%
|
|==================================================================================================== | 60%
|
|=============================================================================================================== | 67%
|
|========================================================================================================================== | 73%
|
|====================================================================================================================================== | 80%
|
|================================================================================================================================================= | 87%
|
|============================================================================================================================================================ | 93%
|
|=======================================================================================================================================================================| 100%
fitting ...
|
| | 0%
|
|=========== | 7%
|
|====================== | 13%
|
|================================= | 20%
|
|============================================= | 27%
|
|======================================================== | 33%
|
|=================================================================== | 40%
|
|============================================================================== | 47%
|
|========================================================================================= | 53%
|
|==================================================================================================== | 60%
|
|=============================================================================================================== | 67%
|
|========================================================================================================================== | 73%
|
|====================================================================================================================================== | 80%
|
|================================================================================================================================================= | 87%
|
|============================================================================================================================================================ | 93%
|
|=======================================================================================================================================================================| 100%
fitting ...
|
| | 0%
|
|=========== | 7%
|
|====================== | 13%
|
|================================= | 20%
|
|============================================= | 27%
|
|======================================================== | 33%
|
|=================================================================== | 40%
|
|============================================================================== | 47%
|
|========================================================================================= | 53%
|
|==================================================================================================== | 60%
|
|=============================================================================================================== | 67%
|
|========================================================================================================================== | 73%
|
|====================================================================================================================================== | 80%
|
|================================================================================================================================================= | 87%
|
|============================================================================================================================================================ | 93%
|
|=======================================================================================================================================================================| 100%
fitting ...
|
| | 0%
|
|=========== | 7%
|
|====================== | 13%
|
|================================= | 20%
|
|============================================= | 27%
|
|======================================================== | 33%
|
|=================================================================== | 40%
|
|============================================================================== | 47%
|
|========================================================================================= | 53%
|
|==================================================================================================== | 60%
|
|=============================================================================================================== | 67%
|
|========================================================================================================================== | 73%
|
|====================================================================================================================================== | 80%
|
|================================================================================================================================================= | 87%
|
|============================================================================================================================================================ | 93%
|
|=======================================================================================================================================================================| 100%
fitting ...
|
| | 0%
|
|=========== | 7%
|
|====================== | 13%
|
|================================= | 20%
|
|============================================= | 27%
|
|======================================================== | 33%
|
|=================================================================== | 40%
|
|============================================================================== | 47%
|
|========================================================================================= | 53%
|
|==================================================================================================== | 60%
|
|=============================================================================================================== | 67%
|
|========================================================================================================================== | 73%
|
|====================================================================================================================================== | 80%
|
|================================================================================================================================================= | 87%
|
|============================================================================================================================================================ | 93%
|
|=======================================================================================================================================================================| 100%
fitting ...
|
| | 0%
|
|=========== | 7%
|
|====================== | 13%
|
|================================= | 20%
|
|============================================= | 27%
|
|======================================================== | 33%
|
|=================================================================== | 40%
|
|============================================================================== | 47%
|
|========================================================================================= | 53%
|
|==================================================================================================== | 60%
|
|=============================================================================================================== | 67%
|
|========================================================================================================================== | 73%
|
|====================================================================================================================================== | 80%
|
|================================================================================================================================================= | 87%
|
|============================================================================================================================================================ | 93%
|
|=======================================================================================================================================================================| 100%
fitting ...
|
| | 0%
|
|=========== | 7%
|
|====================== | 13%
|
|================================= | 20%
|
|============================================= | 27%
|
|======================================================== | 33%
|
|=================================================================== | 40%
|
|============================================================================== | 47%
|
|========================================================================================= | 53%
|
|==================================================================================================== | 60%
|
|=============================================================================================================== | 67%
|
|========================================================================================================================== | 73%
|
|====================================================================================================================================== | 80%
|
|================================================================================================================================================= | 87%
|
|============================================================================================================================================================ | 93%
|
|=======================================================================================================================================================================| 100%
rownames for data not specified, using 1:nrow(data)
summary(intern)
Clustering Methods:
hierarchical kmeans diana fanny som model sota pam clara agnes
Cluster sizes:
2 3 4 5 6 7 8 9
Validation Measures:
2 3 4 5 6 7 8 9
hierarchical Connectivity 4.6155 7.5444 11.6909 14.9532 19.2865 22.4250 25.2083 29.7536
Dunn 0.3720 0.3720 0.1929 0.1929 0.1929 0.2646 0.2704 0.2724
Silhouette 0.8194 0.7297 0.7381 0.6527 0.6414 0.6302 0.6247 0.5973
kmeans Connectivity 10.2603 12.2353 13.9532 26.8333 31.1667 27.6187 30.4020 43.1052
Dunn 0.0382 0.1528 0.1929 0.0645 0.0645 0.0813 0.0821 0.0475
Silhouette 0.7980 0.7422 0.7376 0.5689 0.5584 0.6131 0.6056 0.5357
diana Connectivity 7.9532 13.0996 15.6984 16.7734 21.1067 29.7790 32.5623 33.6690
Dunn 0.1283 0.1123 0.1536 0.2222 0.2327 0.0813 0.0912 0.1128
Silhouette 0.8116 0.7116 0.6998 0.6916 0.6911 0.5705 0.5635 0.5625
fanny Connectivity 5.3575 30.5325 20.0222 40.7393 31.4968 43.5433 57.6595 57.0190
Dunn 0.0806 0.0121 0.0210 0.0211 0.0216 0.0216 0.0235 0.0242
Silhouette 0.7644 0.5162 0.4707 0.3273 0.3990 0.3179 0.2899 0.3029
som Connectivity 10.2603 12.2353 20.5857 34.8214 35.0476 42.3377 42.9198 44.1690
Dunn 0.0382 0.1528 0.0542 0.0242 0.0242 0.0242 0.0235 0.0249
Silhouette 0.7980 0.7422 0.6000 0.4521 0.4280 0.3946 0.4139 0.4297
model Connectivity 18.0730 35.9623 33.8560 39.5698 47.6821 38.4135 64.5921 121.5214
Dunn 0.0265 0.0091 0.0125 0.0171 0.0171 0.0529 0.0305 0.0331
Silhouette 0.6085 0.3435 0.2001 0.2781 0.2833 0.2620 0.3482 -0.0495
sota Connectivity 5.3575 9.9730 15.6730 21.8306 22.4734 23.8901 26.2234 30.3341
Dunn 0.0806 0.1536 0.1536 0.1765 0.2327 0.2327 0.2327 0.2071
Silhouette 0.7644 0.7469 0.7271 0.6824 0.6849 0.6841 0.6833 0.6189
pam Connectivity 7.2980 12.2353 21.0063 29.3655 28.9500 30.6679 35.0012 38.4508
Dunn 0.0784 0.1528 0.0211 0.0216 0.0216 0.0288 0.0288 0.0288
Silhouette 0.7900 0.7422 0.4988 0.4323 0.4729 0.4794 0.4689 0.4507
clara Connectivity 10.3147 16.0972 21.2472 29.6198 29.2012 38.0159 35.2159 37.8175
Dunn 0.0746 0.0562 0.0216 0.0216 0.0235 0.0242 0.0305 0.0363
Silhouette 0.8024 0.6976 0.4861 0.4355 0.4636 0.4693 0.4853 0.4787
agnes Connectivity 4.6155 7.5444 11.6909 14.9532 19.2865 22.4250 25.2083 29.7536
Dunn 0.3720 0.3720 0.1929 0.1929 0.1929 0.2646 0.2704 0.2724
Silhouette 0.8194 0.7297 0.7381 0.6527 0.6414 0.6302 0.6247 0.5973
Optimal Scores:
NA
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
rename( product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste)
no_k = 3
no_var = 3
# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale
# compute dissimilarity matrix with euclidean distances
d <- dist(scale_data, method = 'euclidean')
# hierarchical clustering using Ward's method
res_hc <- hclust(d, method = 'ward.D2')
# cut tree into 3 groups
grp <- cutree(res_hc, k = no_k)
# visualize
plot(res_hc, cex = 0.6) # plot tree
rect.hclust(res_hc, k = no_k, border = 2:5) # add rectangles

# execution of k-means with k = 4
final <- kmeans(scale_data, no_k, nstart = 30)
fviz_cluster(final, data = scale_data) + theme_minimal() + ggtitle("k = 4")

as.tibble(scale_data) %>%
mutate(cluster = final$cluster) %>%
group_by(cluster) %>%
summarise_all('mean')
`as.tibble()` was deprecated in tibble 2.0.0.
Please use `as_tibble()` instead.
The signature and semantics have changed, see `?as_tibble`.
as.tibble(plastic) %>%
mutate(cluster = final$cluster) %>%
group_by(cluster) %>%
summarise_all('mean')
argument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NA
df <- as_tibble(scale_data) %>% rownames_to_column()
cluster_pos <- as_tibble(final$cluster) %>% rownames_to_column()
colnames(cluster_pos) <- c("rowname", "cluster")
final <- inner_join(cluster_pos, df, by = "rowname")
library(ggiraphExtra) # enhance 'ggplot2' and 'ggiraph', provides functions for exploratory plots, see https://rpubs.com/cardiomoon/231820
Attache Paket: 㤼㸱ggiraphExtra㤼㸲
The following object is masked from 㤼㸱package:ggthemes㤼㸲:
theme_clean
radar <- ggRadar(final[-1], aes(group = cluster), rescale = FALSE,
legend.position = "none", size = 1, interactive = FALSE, use.label = TRUE) +
facet_wrap(~cluster) +
scale_y_discrete(breaks = NULL) + # don't show ticks
theme_minimal()
radar

df <- as_tibble(scale_data)
df$cluster <- as.factor(final$cluster)
library(GGally) # extends ggplot2 by adding several functions to reduce the complexity of combining geoms with transformed data
ggpairs(df, 1:no_var, mapping = ggplot2::aes(color = cluster, alpha = 0.5),
diag = list(continuous = wrap("densityDiag")),
lower = list(continuous = wrap("points", alpha = 0.9))) +
theme_minimal()

df <- as_tibble(scale_data)
df$cluster <- as.factor(final$cluster)
box1 <- ggplot(df, aes(x = cluster, y = product, colour = cluster)) +
geom_boxplot() +
theme_minimal() +
ggtitle("product")
box1 <- ggplotly(box1) %>% layout(annotations = list(text = "product", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
box2 <- ggplot(df, aes(x = cluster, y = rigid, colour = cluster)) +
geom_boxplot() +
theme_minimal() +
ggtitle("rigid")
box2 <- ggplotly(box2) %>% layout(annotations = list(text = "rigid", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
box3 <- ggplot(df, aes(x = cluster, y = flexible, colour = cluster)) +
geom_boxplot() +
theme_minimal() +
ggtitle("flexible")
box3 <- ggplotly(box3) %>% layout(annotations = list(text = "flexible", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
fig <- subplot(box1, box2, box3, margin = 0) %>% layout()
fig
scale is generic function whose default method centers and/or scales the columns of a numeric matrix normally you scale the columns (ie variables) to have all variables on the same scale st an increase in 1 unit has the same meaning across all variables, the shape / distribution will not change, but the axis units (see scatter plots) scaling the rows (ie observations) made all observations equal in the sense that the absolute distances were replaced by relative distances across all observations, st now the biggest value in a row was across all rows equally big (see “dataframe” plot), in our case that was the production variable, now rigid and flexible were separated if there was more or less of them relative to the total production (biggest value, well actually to the mean not the max, but never mind), this sorted all observations according to be more on the rigid or flexible side, and that was excatly what I was looking for (and why I was too happy to see my mistake in the first place)
df <- as_tibble(scale_data)
df$cluster <- as.factor(final$cluster)
library(GGally) # extends ggplot2 by adding several functions to reduce the complexity of combining geoms with transformed data
# https://www.r-graph-gallery.com/parallel-plot-ggally.html#custom
parcoord_plot <- ggparcoord(df,
columns = 1:no_var, groupColumn = (no_var+1),
scale='center', # scaling: standardize and center variables
showPoints = TRUE,
title = name,
alphaLines = 0.3) +
theme_minimal()
parcoord_plot <- ggplotly(parcoord_plot) %>% layout(autosize=T)
parcoord_plot
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
rename( product = production_of_in_scope_polymers,
flexible = flexible_format_contribution_to_sup_waste,
rigid = rigid_format_contribution_to_sup_waste)
scale_data <- as_tibble(t(scale(t(df[, !names(df) %in% name])))) # We need to normalize the data based on scale function because the variables are different scales; Normalization means subtracting mean from each observation and dividing with standard deviation. Check all the variables mean values are zero now
head(scale_data)
scale_data <- as_tibble(scale(df[, !names(df) %in% name]))
head(scale_data)
head(t(df[, !names(df) %in% name]))
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26] [,27] [,28] [,29]
product 11.2 9.3 11.6 5.1 9.5 8.8 9.3 5.5 6.7 2.5 5.0 4.1 4.8 4.5 1.7 1.6 3.6 3.4 3.1 1.4 1.9 2.4 2.4 1 2.0 1.0 2.2 1.6 1.8
flexible 4.7 4.7 4.0 0.2 3.2 3.3 2.1 1.8 1.9 0.0 1.5 1.1 1.0 1.0 0.0 0.0 1.0 1.2 1.1 0.0 1.0 0.9 0.6 0 0.7 0.0 0.5 0.7 0.7
rigid 1.2 0.9 1.3 4.5 1.1 0.8 1.8 1.3 1.1 2.3 0.7 1.0 1.0 0.9 1.6 1.6 0.6 0.3 0.3 1.3 0.2 0.3 0.5 1 0.3 0.9 0.4 0.2 0.1
[,30] [,31] [,32] [,33] [,34] [,35] [,36] [,37] [,38] [,39] [,40] [,41] [,42] [,43] [,44] [,45] [,46] [,47] [,48] [,49] [,50] [,51] [,52] [,53] [,54] [,55] [,56] [,57]
product 1.2 1.7 1.3 1.9 1.6 1.4 1.0 1.1 1.2 0.6 1.0 1.5 1.1 1.2 0.5 1.1 1.2 0.6 0.5 1.1 1.0 1.0 0.4 0.9 0.8 0.9 0.7 0.6
flexible 0.2 0.5 0.4 0.6 0.6 0.5 0.5 0.2 0.5 0.0 0.5 0.4 0.4 0.5 0.0 0.4 0.4 0.0 0.0 0.3 0.4 0.3 0.0 0.3 0.3 0.3 0.3 0.3
rigid 0.7 0.4 0.4 0.2 0.1 0.2 0.1 0.4 0.1 0.6 0.1 0.1 0.2 0.1 0.5 0.1 0.1 0.5 0.5 0.2 0.1 0.1 0.4 0.1 0.1 0.1 0.0 0.1
[,58] [,59] [,60] [,61] [,62] [,63] [,64] [,65] [,66] [,67] [,68] [,69] [,70] [,71] [,72] [,73] [,74] [,75] [,76] [,77] [,78] [,79] [,80] [,81] [,82] [,83] [,84] [,85]
product 0.4 0.8 0.7 0.6 0.9 0.4 0.6 0.7 1.0 0.6 0.8 0.3 0.7 0.7 0.5 0.5 0.6 0.4 0.3 0.7 0.7 0.7 0.4 0.4 0.6 0.5 0.5 0.5
flexible 0.0 0.2 0.3 0.1 0.2 0.0 0.1 0.2 0.2 0.2 0.1 0.0 0.2 0.2 0.0 0.2 0.2 0.2 0.0 0.2 0.2 0.2 0.2 0.3 0.2 0.2 0.2 0.2
rigid 0.4 0.1 0.1 0.2 0.1 0.3 0.2 0.1 0.1 0.1 0.2 0.3 0.1 0.1 0.2 0.1 0.1 0.0 0.3 0.1 0.1 0.1 0.0 0.0 0.0 0.1 0.0 0.1
[,86] [,87] [,88] [,89] [,90] [,91] [,92] [,93] [,94] [,95] [,96] [,97] [,98] [,99] [,100]
product 0.2 0.6 0.4 0.5 0.5 0.6 0.5 0.2 0.2 0.2 0.5 0.5 0.6 0.2 0.2
flexible 0.0 0.2 0.2 0.1 0.1 0.2 0.2 0.0 0.0 0.0 0.1 0.1 0.1 0.0 0.0
rigid 0.2 0.1 0.0 0.1 0.1 0.1 0.0 0.2 0.2 0.2 0.1 0.1 0.1 0.2 0.2
library(caret)
preObj <- preProcess(df[, !names(df) %in% name], method=c("center", "scale"))
newData <- predict(preObj, df[, !names(df) %in% name])
head(newData)
# find more methods here: https://stackoverflow.com/questions/15215457/standardize-data-columns-in-r
LS0tDQp0aXRsZTogImRpc2NvdmVyIGFuZCB0cmFuc2Zvcm0gcGxhc3RpYyB3YXN0ZSBtYWtlcnMgaW5kZXggZGF0YSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCi0tLQ0KcHVycG9zZSBvZiBub3RlYm9vaw0KLS0tDQoNCiAgKC0pIHRlc3QgYW5kIHBsYXkgd2l0aCBhZHZhbmNlZCBudW1lcmljYWwgRURBIG1ldGhvZHMNCiAgDQp0b2RvczoNCiAgKC0pIC4uLg0KICANCi0tLQ0KaW5mb3JtYXRpb24NCi0tLQ0KDQpuYW1lOiBtYWtlb3Zlcm1vbmRheV8yMDIxdzIyDQpsaW5rOiBodHRwczovL2RhdGEud29ybGQvbWFrZW92ZXJtb25kYXkvMjAyMXcyMg0KdGl0bGU6IDIwMjEvVzIyOiBUaGUgUGxhc3RpYyBXYXN0ZSBNYWtlcnMgSW5kZXgNCkRhdGEgU291cmNlOiBbTWluZGVyb29dKGh0dHBzOi8vd3d3Lm1pbmRlcm9vLm9yZy9wbGFzdGljLXdhc3RlLW1ha2Vycy1pbmRleC9kYXRhL2luZGljZXMvcHJvZHVjZXJzLykgZnJvbSAyMDE5DQogIA0KLS0tDQppbnNpZ2h0cyANCi0tLQ0KDQogIChpKSBjb3JyZWxhdGlvbiAtIG1vc3Qgb2YgdGhlIGNvbHVtbnMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCB0aGF0IHdhcyB0byBiZSBleHBlY3RlZCwgc2luY2UgbW9zdCB2YXJpYWJsZXMgYXJlIGRlcGVuZCBvbiBlYWNoIG90aGVyLCBlLmcuLCByaWdpZCArIGZsZXhpYmxlID0gdG90YWwgYyBwcm9kdWN0aW9uLCAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbCAtPiAtcmFuaw0KICAgICAgICAgICAgICAgICAgICByaWdpZCBvdmVyYWxsIGhhcyBhIGxlc3Mgc3Ryb25nIGNvcnJlbGF0aW9uIHdpdGggdGhlIG90aGVyIHZhcmlhYmxlcywgd2hpY2ggbWlnaHQgaGludCB0byB0aGVyZSBiZWluZyBhIGEgZGlmZmVyZW50IHN1Yi1wb3B1bGF0aW9uIGJhc2VkIG9uIHJpZ2lkIHByb2R1Y3Rpb24sIA0KICAgICAgICAgICAgICAgICAgICBmbGV4aWJsZSBoYXMgYSBzdHJvbmdlciBjb3JyZWxhdGlvbiB3aXRoIHRvdGFsIGFzIHJpZ2lkIGFuZCB0b3RhbCwgc2luY2UgZmxleGlibGUgaXMgYSBmYXIgYmlnZ2VyIGNvbnRyaWJ1dGlvbiB0byB0b3RhbA0KICAgICAgICAgICAgICAgICAgICBmbGV4aWJsZSBoYXMgYSBzdHJvbmdlciBjb3JyZWxhdGlvbiB3aXRoIHRoZSBvdmVyYWxsIHByb2R1Y3Rpb24sIHRoYW4gcmlnaWQsIHRoaXMgaXMgaW50ZXJlc3RpbmcgYW5kIGFsc28gbWlnaHQgaGludCB0byByaWdpZCBwcm9kdWNlcnMgaGF2ZSBhIGRpZmZlcmVudCBtYXJrZXQgICAgICAgICAgICAgICAgICAgICAgICBzdHJhdGVneSB0aGFuIGZsZXhpYmxlIHByb2R1Y2Vycw0KICAoaSkgYSBHaW5pIGNvZWZmaWNpZW50IG9mIDAuNTYgaXMgcXVpdGUgaGlnaCwgd2hpY2ggbWVhbnMgdGhhdCBvbmx5IGEgdG9wIGZldyBwcm9kdWNlcnMgYXJlIHJlc3BvbnNpYmxlIGZvciBhIGxhcmdlIGFtb3VudCBvZiB0aGUgdG90YWwgU1VQIHdhc3RlIGNvbnRyaWJ1dGlvbiwgaW4gb3RoZXIgd29yZHMgMTAlICAgICAgICAgYXJlIHJlc3BvbnNpYmxlIGZvciA0NCUgb2YgdGhlIHdhc3RlDQogIChpKSBJIG5lZWQgbW9yZSBrbm93bGVkZ2UgaG93IHRvIHdvcmsgd2l0aCBhbmQgaW50ZXJwcmV0IFNPTSBhbmQgUENBLCBtYXliZSBhbHNvIG5vdCBlbm91Z2ggb2JzZXJ2YXRpb25zIGluIGRhdGEgc2V0LCAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgYnV0IHRoZSBjbHVzdGVyaW5nIGZyb20gU09NLCB3aGVuIG9ubHkgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFyZSB1c2VkLCBhY3R1YWxseSBzaG93IHRoZSB0d28gYXNzdW1lZCBzdWJwb3B1bGF0aW9ucyBiYXNlZCBvbiByaWdpZCB2cyBmbGV4aWJsZSBwcm9kdWN0aW9uDQogIChpKSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QgaXMgbm90IHZlcnkgaW5zaWdodGZ1bCwgc2luY2UgdGhlcmUgaXMgc28gbXVjaCBkYXRhIGNsb3NlIHRvZ2V0aGVyLCB3ZSBjYW5ub3Qgc2VlIGFueSBncm91cGluZyB3aXRob3V0IHByaW9yIGtub3dsZWRnZSwgb25seSBoaW50IGlzIHdoZW4geW91IHpvb20gaW4gYXQgICAgICAgdGhlIGJ1bGsgb2YgdGhlIGRhdGEgaW4gdGhlIGxvd2VyIHRoaXJkICh5IGRpcmVjdGlvbiksIHRoZXJlIGlzIGEgbmVnYXRpdmUgY29ycmVsYXRpb24NCiAgKGkpIHJlbWluZCB0aGF0IHRoZSBkaWZmZXJlbmNlIHdpdGggdGhlIHBhcnRpdGlvbiBieSBrLW1lYW5zIGlzIHRoYXQgZm9yIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCB0aGUgbnVtYmVyIG9mIGNsYXNzZXMgaXMgbm90IHNwZWNpZmllZCBpbiBhZHZhbmNlICAgDQogICAgICBpdCBzZWVtcyB0aGF0IG1vc3QgY2x1c3RlcnMgYXJlIGNvbmZ1c2VkIGJ5IHNvbWUgc3BlY2lmaWMgdmFsdWVzICg4MSwgNjMsIDcyLCAzNj8pLCBhcmUgdGhleSBzb21lIHNvcnQgb2Ygb3V0bGllciwgb3Igd2hhdCBpcyBzcGVjaWFsIGFib3V0IHRoZW0/DQogIChpKSBBcyBhIHJlbWluZGVyLCB0aGlzIG1ldGhvZCBhaW1zIGF0IHBhcnRpdGlvbmluZyBuIG9ic2VydmF0aW9ucyBpbnRvIGsgY2x1c3RlcnMgaW4gd2hpY2ggZWFjaCBvYnNlcnZhdGlvbiBiZWxvbmdzIHRvIHRoZSBjbHVzdGVyIHdpdGggdGhlIGNsb3Nlc3QgYXZlcmFnZSwgc2VydmluZyBhcyBhIHByb3RvdHlwZSAgICAgICBvZiB0aGUgY2x1c3Rlcg0KICAgICAgd29ya3Mgbm90IHF1aXRlIGFzIHdlbGwgYXMgU09NLCBidXQgdmVyeSBjbG9zZSwgYWxzbyByZWNvbW1lbmQgY2x1c3RlciBpcyB0aHJlZSwgYnV0IGNsb3NlbHkgZm9sbG93ZWQgYnkgMiwgc2luY2UgMyBkb2VzIG5vdCByZXZlYWwgYW55IHNpZ25pZmljYW50IGNvbm5lY3Rpb24gcmlnaHQgbm93LCBJICAgICAgICAgICAgd29uZGVyIHdoYXQgdGhhdCBzaG93cw0KICAgICAgDQotLS0NCnJlZmVyZW5jZXMNCi0tLQ0KICANCiAgKGkpIGh0dHBzOi8vaWFtY2llcmEuZ2l0aHViLmlvL1NPTWV4YW1wbGUvaHRtbC9TT01fUk5Bc2VxX3R1dG9yaWFsX3BhcnQyYV9TT00uaHRtbA0KICAoaSkgaHR0cHM6Ly9zdGF0c2FuZHIuY29tL2Jsb2cvY2x1c3RlcmluZy1hbmFseXNpcy1rLW1lYW5zLWFuZC1oaWVyYXJjaGljYWwtY2x1c3RlcmluZy1ieS1oYW5kLWFuZC1pbi1yLyNvcHRpbWFsLW51bWJlci1vZi1jbHVzdGVycw0KICAoaSkgaHR0cHM6Ly9sdWtlZGFuaWVsczEuZ2l0aHViLmlvL0JpbzM4MV8yMDE4L0RhbmllbHNfQ2x1c3Rlcl9BbmFseXNpc19MZWN0dXJlLmh0bWwNCiAgKGkpIGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS8xMC10aXBzLWZvci1jaG9vc2luZy10aGUtb3B0aW1hbC1udW1iZXItb2YtY2x1c3RlcnMtMjc3ZTkzZDcyZDkyDQogICAgICANCi0tLQ0KbG9hZCBwYWNrYWdlcyAmIGdsb2JhbCBvcHRpb25zDQotLS0NCmBgYHtyLCBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKSAjIHRpZHkgZGF0YSBmcmFtZQ0KbGlicmFyeShnZ3RoZW1lcykgIyBmb3IgZXh0cmEgcGxvdCB0aGVtZXMNCmxpYnJhcnkocGxvdGx5KSAjIG1ha2UgZ2dwbG90cyBpbnRlcmFjdGl2ZQ0KDQpsaWJyYXJ5KGZhY3RvZXh0cmEpICMgcHJvdmlkZXMgc29tZSBlYXN5LXRvLXVzZSBmdW5jdGlvbnMgdG8gZXh0cmFjdCBhbmQgdmlzdWFsaXplIHRoZSBvdXRwdXQgb2YgbXVsdGl2YXJpYXRlIGRhdGEgYW5hbHlzZXMNCg0KIyBpbmRpdmlkdWFsIGxpYnJhcmllcyBhcmUgaW4gdGhlIGFjY29yZGluZyBjZWxsDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgIyBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDksIA0KICB3YXJuaW5nID0gRkFMU0UNCikNCg0KIyBwbG90bHk6ICx3aWR0aCA9IDkwMCwgaGVpZ2h0ID0gNTUwDQpgYGANCg0KLS0tDQpvdmVydmlldw0KLS0tDQpgYGB7cn0NCmhlYWQocGxhc3RpYykNCmBgYA0KYGBge3J9DQpzdW1tYXJ5KHBsYXN0aWMpDQpgYGANCg0KLS0tDQpjb3JyZWxhdGlvbiANCi0tLQ0KbW9zdCBvZiB0aGUgY29sdW1ucyBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQsIHRoYXQgd2FzIHRvIGJlIGV4cGVjdGVkLCBzaW5jZSBtb3N0IHZhcmlhYmxlcyBhcmUgZGVwZW5kIG9uIGVhY2ggb3RoZXIsIGUuZy4sIHJpZ2lkICsgZmxleGlibGUgPSB0b3RhbCBjIHByb2R1Y3Rpb24sIHRvdGFsIC0+IC1yYW5rDQpyaWdpZCBvdmVyYWxsIGhhcyBhIGxlc3Mgc3Ryb25nIGNvcnJlbGF0aW9uIHdpdGggdGhlIG90aGVyIHZhcmlhYmxlcywgd2hpY2ggbWlnaHQgaGludCB0byB0aGVyZSBiZWluZyBhIGEgZGlmZmVyZW50IHN1Yi1wb3B1bGF0aW9uIGJhc2VkIG9uIHJpZ2lkIHByb2R1Y3Rpb24sIA0KZmxleGlibGUgaGFzIGEgc3Ryb25nZXIgY29ycmVsYXRpb24gd2l0aCB0b3RhbCBhcyByaWdpZCBhbmQgdG90YWwsIHNpbmNlIGZsZXhpYmxlIGlzIGEgZmFyIGJpZ2dlciBjb250cmlidXRpb24gdG8gdG90YWwNCmZsZXhpYmxlIGhhcyBhIHN0cm9uZ2VyIGNvcnJlbGF0aW9uIHdpdGggdGhlIG92ZXJhbGwgcHJvZHVjdGlvbiwgdGhhbiByaWdpZCwgdGhpcyBpcyBpbnRlcmVzdGluZyBhbmQgYWxzbyBtaWdodCBoaW50IHRvIHJpZ2lkIHByb2R1Y2VycyBoYXZlIGEgZGlmZmVyZW50IG1hcmtldCBzdHJhdGVneSB0aGFuIGZsZXhpYmxlIHByb2R1Y2Vycw0KDQolPiUgc2VsZWN0KC1yYW5rLCAtdG90YWwsIC1hc3NldHMpIGNhbiBzaG93IGEgbW9yZSBjbGVhciBwaWN0dXJlIGJ5IHJlbW92aW5nIGRlcGVuZGVudCB2YXJpYWJsZXMNCg0KYGBge3J9DQpuYW1lID0gYygnJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtcG9seW1lcl9wcm9kdWNlciwgLSB0b3RhbF93YXN0ZV9kaXZfcHJvZHVjdGlvbikgJT4lIG11dGF0ZShyYW5rID0gLXJhbmspICU+JSAjIGNoYW5nZSBzaWduIG9mIHJhbmsgdG8gbWFrZSBpdCBpbmNyZWFzZSB3aXRoIHRoZSBkZXBlbmRlbnQgdmFyaWFibGVzDQogIHJlbmFtZSggYXNzZXRzID0gbm9fb2ZfYXNzZXRzLCANCiAgICAgICAgICBwcm9kdWN0ID0gcHJvZHVjdGlvbl9vZl9pbl9zY29wZV9wb2x5bWVycywgDQogICAgICAgICAgZmxleGlibGUgPSBmbGV4aWJsZV9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgDQogICAgICAgICAgcmlnaWQgPSByaWdpZF9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgDQogICAgICAgICAgdG90YWwgPSB0b3RhbF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKSANCg0KDQpsaWJyYXJ5KGNvcnJwbG90KSAjIGNvcnJlbGF0aW9uIHBsb3RzDQojIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jb3JycGxvdC92aWduZXR0ZXMvY29ycnBsb3QtaW50cm8uaHRtbA0KDQpjb3IgPC0gY29yKGRmKQ0KY29yX210ZXN0IDwtIGNvci5tdGVzdChkZiwgY29uZi5sZXZlbCA9IDAuOTkpICMgY29tYmluaW5nIGNvcnJlbG9ncmFtIHdpdGggc2lnbmlmaWNhbmNlIHRlc3QNCmNvcnJwbG90KGNvciwgbWV0aG9kID0gIm51bWJlciIsIG9yZGVyID0gJ2hjbHVzdCcsIGFkZHJlY3QgPSAzLCBwLm1hdCA9IGNvcl9tdGVzdCRwLCBpbnNpZyA9ICJwY2giKSAjIG9yZGVyID0gQU9FLCBGUEMsIGhjbHVzdCArIGFkZHJlY3QNCg0KY29ycnBsb3QoY29yLCBwLm1hdCA9IGNvcl9tdGVzdCRwLCBsb3cgPSBjb3JfbXRlc3QkbG93Q0ksIHVwcCA9IGNvcl9tdGVzdCR1cHBDSSwgb3JkZXIgPSAnaGNsdXN0Jywgc2lnLmxldmVsID0gMC4wMSwgdGwucG9zID0gJ2QnLCBhZGRyZWN0ID0gMywgcmVjdC5jb2wgPSAnbmF2eScsIHBsb3RDID0gJ3JlY3QnLCBjbC5wb3MgPSAnbicsIGluc2lnID0gInAtdmFsdWUiKQ0KYGBgDQoNCi0tLQ0KTG9yZW56IGN1cnZlICYgR2luaSBjb2VmZmljaWVudA0KLS0tDQphIEdpbmkgY29lZmZpY2llbnQgb2YgMC41NiBpcyBxdWl0ZSBoaWdoLCB3aGljaCBtZWFucyB0aGF0IG9ubHkgYSB0b3AgZmV3IHByb2R1Y2VycyBhcmUgcmVzcG9uc2libGUgZm9yIGEgbGFyZ2UgYW1vdW50IG9mIHRoZSB0b3RhbCBTVVAgd2FzdGUgY29udHJpYnV0aW9uLCBpbiBvdGhlciB3b3JkcyAxMCUgYXJlIHJlc3BvbnNpYmxlIGZvciA0NCUgb2YgdGhlIHdhc3RlDQoNCg0KYGBge3J9DQpuYW1lID0gYygndG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZScsICdQcm9kdWNlcnMnKQ0KZGYgPC0gcGxhc3RpYyAlPiUgcmVuYW1lKHZhbHVlID0gdG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSkgJT4lIHNlbGVjdCh2YWx1ZSkgDQoNCmxpYnJhcnkoZ2dsb3JlbnopICN0cmFuc2Zvcm1hdGlvbnMgZm9yIHBsb3R0aW5nIExvcmVueiBjdXJ2ZSwgaHR0cHM6Ly9naXRodWIuY29tL2pqY2hlcm4vZ2dsb3JlbnoNCg0KbG9yZW56Y3VydmUgPC0gZGYgJT4lIA0KICBnZ3Bsb3QoYWVzKHZhbHVlKSkgKw0KICAgIHN0YXRfbG9yZW56KGRlc2MgPSBGQUxTRSkgKw0KICAgIGNvb3JkX2ZpeGVkKCkgKw0KICAgIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIGhyYnJ0aGVtZXM6OnNjYWxlX3hfcGVyY2VudCgpICsNCiAgICBocmJydGhlbWVzOjpzY2FsZV95X3BlcmNlbnQoKSArDQogICAgIyBocmJydGhlbWVzOjp0aGVtZV9pcHN1bV9yYygpICsNCiAgICBhbm5vdGF0ZV9pbmVxKGRmJHZhbHVlKSArDQogICAgZ2d0aXRsZShwYXN0ZSgiTG9yZW56IGN1cnZlIGZvciIsIG5hbWVbMV0sIHNlcD0iICIpKSANCmxvcmVuemN1cnZlIDwtIGdncGxvdGx5KGxvcmVuemN1cnZlKSAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9IHBhc3RlKCJjdW11bGF0aXZlIHBlcmNlbnRhZ2Ugb2YiLCBuYW1lWzFdLCBzZXA9IiAiKSksIHhheGlzID0gbGlzdCh0aXRsZSA9IHBhc3RlKCJjdW11bGF0aXZlIHBlcmNlbnRhZ2Ugb2YiLCBuYW1lWzJdLCBzZXA9IiAiKSkpIA0KDQpsb3JlbnpjdXJ2ZQ0KYGBgDQpgYGB7cn0NCm5hbWUgPSBjKCdmbGV4aWJsZV9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZScsICdyaWdpZF9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZScsICdQcm9kdWNlcnMnLCAnZmxleGlibGUnLCAncmlnaWQnKQ0KZGYgPC0gcGxhc3RpYyAlPiUgcmVuYW1lKGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSBzZWxlY3QoZmxleGlibGUsIHJpZ2lkKSAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGZsZXhpYmxlLHJpZ2lkKSkNCg0KbGlicmFyeShnZ2xvcmVueikgI3RyYW5zZm9ybWF0aW9ucyBmb3IgcGxvdHRpbmcgTG9yZW56IGN1cnZlLCBodHRwczovL2dpdGh1Yi5jb20vampjaGVybi9nZ2xvcmVueg0KDQojIGdldCBnZ3Bsb3Qgc3RhbmRhcmQgY29sb3JzIGZvciBncm91cGluZywgd2hpY2ggYXJlIGVxdWFsbHkgc3BhY2VkIGh1ZXMgYXJvdW5kIHRoZSBjb2xvciB3aGVlbCwgc3RhcnRpbmcgZnJvbSAxNQ0KZ2dfY29sb3JfaHVlIDwtIGZ1bmN0aW9uKG4pIHsNCiAgaHVlcyA9IHNlcSgxNSwgMzc1LCBsZW5ndGggPSBuICsgMSkNCiAgaGNsKGggPSBodWVzLCBsID0gNjUsIGMgPSAxMDApWzE6bl0NCn0NCg0KbG9yZW56Y3VydmUgPC0gZGYgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3IgPSBuYW1lKSkgKw0KICAgIHN0YXRfbG9yZW56KGRlc2MgPSBGQUxTRSkgKw0KICAgIGNvb3JkX2ZpeGVkKCkgKw0KICAgIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIGhyYnJ0aGVtZXM6OnNjYWxlX3hfcGVyY2VudCgpICsNCiAgICBocmJydGhlbWVzOjpzY2FsZV95X3BlcmNlbnQoKSArDQogICAgIyBocmJydGhlbWVzOjp0aGVtZV9pcHN1bV9yYygpICsNCiAgICBhbm5vdGF0ZV9pbmVxKGZpbHRlcihkZiwgbmFtZSA9PSBuYW1lWzRdKSR2YWx1ZSwgeSA9IDAuOTUsIGNvbG91ciA9IGdnX2NvbG9yX2h1ZSgyKVsxXSkgKw0KICAgIGFubm90YXRlX2luZXEoZmlsdGVyKGRmLCBuYW1lID09IG5hbWVbNV0pJHZhbHVlLCB5ID0gMC45MCwgY29sb3VyID0gZ2dfY29sb3JfaHVlKDIpWzJdKSArDQogICAgZ2d0aXRsZShwYXN0ZSgiY29tcGFyZSBMb3JlbnogY3VydmUgb2YiLCBuYW1lWzFdLCAiYW5kIiwgbmFtZVsyXSwgc2VwPSIgIikpIA0KbG9yZW56Y3VydmUgPC0gZ2dwbG90bHkobG9yZW56Y3VydmUpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHRpdGxlID0gcGFzdGUoImN1bXVsYXRpdmUgcGVyY2VudGFnZSBvZjxicj4iLCBuYW1lWzFdLCAiPGJyPiIsIG5hbWVbMl0sIHNlcD0iIikpLCB4YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZSgiY3VtdWxhdGl2ZSBwZXJjZW50YWdlIG9mIiwgbmFtZVsyXSwgc2VwPSIgIikpKSANCg0KbG9yZW56Y3VydmUNCmBgYA0KDQotLS0NCnByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgY29sb3JlZCBieSBzZWxmIG9yZ2FuaXppbmcgbWFwIGNsdXN0ZXINCi0tLQ0KSSBuZWVkIG1vcmUga25vd2xlZGdlIGhvdyB0byB3b3JrIHdpdGggYW5kIGludGVycHJldCBTT00gYW5kIFBDQSwgbWF5YmUgYWxzbyBub3QgZW5vdWdoIG9ic2VydmF0aW9ucyBpbiBkYXRhIHNldA0KaHR0cHM6Ly9pYW1jaWVyYS5naXRodWIuaW8vU09NZXhhbXBsZS9odG1sL1NPTV9STkFzZXFfdHV0b3JpYWxfcGFydDJhX1NPTS5odG1sDQoNCmJ1dCB0aGUgY2x1c3RlcmluZyBmcm9tIFNPTSwgd2hlbiBvbmx5IGluZGVwZW5kZW50IHZhcmlhYmxlcyBhcmUgdXNlZCwgYWN0dWFsbHkgc2hvdyB0aGUgdHdvIGFzc3VtZWQgc3VicG9wdWxhdGlvbnMgYmFzZWQgb24gcmlnaWQgdnMgZmxleGlibGUgcHJvZHVjaXRvbg0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCiMgV2UgbmVlZCB0byBub3JtYWxpemUgdGhlIGRhdGEgYmFzZWQgb24gc2NhbGUgZnVuY3Rpb24gYmVjYXVzZSB0aGUgdmFyaWFibGVzIGFyZSBkaWZmZXJlbnQgc2NhbGVzOyBOb3JtYWxpemF0aW9uIG1lYW5zIHN1YnRyYWN0aW5nIG1lYW4gZnJvbSBlYWNoIG9ic2VydmF0aW9uIGFuZCBkaXZpZGluZyB3aXRoIHN0YW5kYXJkIGRldmlhdGlvbi4gQ2hlY2sgYWxsIHRoZSB2YXJpYWJsZXMgbWVhbiB2YWx1ZXMgYXJlIHplcm8gbm93DQoNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgodChzY2FsZSh0KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkpKQ0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChzY2FsZShkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQpzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgIyB3aXRob3V0IHNjYWxpbmcgc2luY2UgdGhlIHZhcmlhYmxlcyBhY3R1YWxseSBhcmUgb24gdGhlIHNhbWUgc2NhbGUNCg0KaGVhZChzY2FsZV9kYXRhKQ0KDQoNCmxpYnJhcnkoa29ob25lbikgIyBmdW5jdGlvbnMgdG8gdHJhaW4gc2VsZi1vcmdhbmlzaW5nIG1hcHMgKFNPTXMpDQoNCiMgcHJpbmNpcGxlIGNvbXBvbmVudCBhbmFseXNpcw0KcGNhIDwtIHByY29tcChzY2FsZV9kYXRhLCBzY2FsZT1GQUxTRSkNCnN1bW1hcnkocGNhKQ0KDQojIHZpc3VhbGl6ZSBwY3MgcmVzdWx0cw0KIyBDb250cmlidXRpb25zIG9mIHZhcmlhYmxlcyB0byBQQzENCmZ2aXpfY29udHJpYihwY2EsIGNob2ljZSA9ICJ2YXIiLCBheGVzID0gMSwgdG9wID0gMTApDQojIENvbnRyaWJ1dGlvbnMgb2YgdmFyaWFibGVzIHRvIFBDMg0KZnZpel9jb250cmliKHBjYSwgY2hvaWNlID0gInZhciIsIGF4ZXMgPSAyLCB0b3AgPSAxMCkNCiMgQ29udHJvbCB2YXJpYWJsZSBjb2xvcnMgdXNpbmcgdGhlaXIgY29udHJpYnV0aW9ucyB0byB0aGUgcHJpbmNpcGxlIGF4aXMNCmZ2aXpfcGNhX3ZhcihwY2EsIGNvbC52YXI9ImNvbnRyaWIiLA0KICAgICAgICAgICAgIGdyYWRpZW50LmNvbHMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIpLA0KICAgICAgICAgICAgIHJlcGVsID0gVFJVRSAjIEF2b2lkIHRleHQgb3ZlcmxhcHBpbmcNCiAgICAgICAgICAgICApICsgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgiVmFyaWFibGVzIC0gUENBIikNCg0KIyBhZGQgYmFjayB0byBvcmlnaW5hbCBzbyBldmVyeXRoaW5nIGlzIHRvZ2V0aGVyDQpwY2Ffc2NvcmVzIDwtIGRhdGEuZnJhbWUocGNhJHgpDQpkYXRhX3ZhbCA8LSBjYmluZChkZiwgcGNhX3Njb3JlcykNCg0KcGNhX3Bsb3QgPC0gZ2dwbG90KGRhdGFfdmFsLCBhZXMoeCA9IFBDMSwgeSA9IFBDMikpICsNCiAgICBnZW9tX3J1ZyhhbHBoYSA9IDAuNSkgKyAjIHR3byAxZCBtYXJnaW5hbCBkaXN0cmlidXRpb25zLCBkaXNwbGF5IGluZGl2aWR1YWwgY2FzZXMgc28gYXJlIGJlc3QgdXNlZCB3aXRoIHNtYWxsZXIgZGF0YXNldHMNCiAgICBnZW9tX2RlbnNpdHlfMmQoYWxwaGEgPSAwLjIsIGJpbnMgPSA0KSArIyAyRCBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uIHVzaW5nIE1BU1M6OmtkZTJkKCkgYW5kIGRpc3BsYXkgdGhlIHJlc3VsdHMgd2l0aCBjb250b3Vycw0KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjc1KSArICMgcG9pbnQgZ2VvbSBpcyB1c2VkIHRvIGNyZWF0ZSBzY2F0dGVycGxvdHMNCiAgICB0aGVtZV9taW5pbWFsKCkNCnBjYV9wbG90IDwtIGdncGxvdGx5KHBjYV9wbG90KSAlPiUgbGF5b3V0KCkNCg0KcGNhX3Bsb3QNCg0KIyBjbHVzdGVyaW5nIGlzIHBlcmZvcm1lZCB1c2luZyB0aGUgc29tKCkgZnVuY3Rpb24gb24gdGhlIHNjYWxlZCBnZW5lIGV4cHJlc3Npb24gdmFsdWVzLg0Kc2V0LnNlZWQoMykNCg0KIyBkZWZpbmUgYSBncmlkIGZvciB0aGUgU09NIGFuZCB0cmFpbg0KZ3JpZF9zaXplIDwtIG5jb2woc2NhbGVfZGF0YSkNCnNvbV9ncmlkIDwtIHNvbWdyaWQoeGRpbSA9IGdyaWRfc2l6ZSwgeWRpbSA9IGdyaWRfc2l6ZSwgdG9wbyA9ICdoZXhhZ29uYWwnKQ0Kc29tX21vZGVsIDwtIHNvbShzY2FsZV9kYXRhLCBncmlkID0gc29tX2dyaWQpDQpzdW1tYXJ5KHNvbV9tb2RlbCkNCg0KIyBnZW5lcmF0ZSBzb20gcGxvdHMgYWZ0ZXIgdHJhaW5pbmcNCnBsb3Qoc29tX21vZGVsLCB0eXBlID0gJ21hcHBpbmcnKQ0KcGxvdChzb21fbW9kZWwsIHR5cGUgPSAnY29kZXMnKQ0KIyBwbG90KHNvbV9tb2RlbCwgdHlwZSA9ICdjb3VudHMnKQ0KIyBwbG90KHNvbV9tb2RlbCwgdHlwZSA9ICdkaXN0Lm5laWdoYm91cnMnKQ0KIyBwbG90KHNvbV9tb2RlbCwgdHlwZSA9ICdxdWFsaXR5JykNCiMgcGxvdChzb21fbW9kZWwsIHR5cGUgPSAnY2hhbmdlcycpDQoNCiMgZnVydGhlciBzcGxpdCB0aGUgY2x1c3RlcnMgaW50byBhIHNtYWxsZXIgc2V0IG9mIGNsdXN0ZXJzIHVzaW5nIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLg0Kc29tX2NsdXN0ZXIgPC0gY3V0cmVlKGhjbHVzdChkaXN0KHNvbV9tb2RlbCRjb2Rlc1tbMV1dKSksIDIpICMgdXNlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHRvIGNsdXN0ZXIgdGhlIGNvZGVib29rIHZlY3RvcnMNCg0KcGxvdChzb21fbW9kZWwsIHR5cGU9Im1hcHBpbmciLCBiZ2NvbCA9IHNvbV9jbHVzdGVyLCBtYWluID0gIkNsdXN0ZXJzIikNCmFkZC5jbHVzdGVyLmJvdW5kYXJpZXMoc29tX21vZGVsLCBzb21fY2x1c3RlcikNCg0KIyBhdHRhY2ggdGhlIGhpZXJjaGFsIGNsdXN0ZXIgdG8gdGhlIGxhcmdlciBkYXRhc2V0IGRhdGFfdmFsLg0KZ3JpZFNxdWFyZSA8LSBncmlkX3NpemUgKiBncmlkX3NpemUNCnNvbV9jbHVzdGVyS2V5IDwtIGRhdGEuZnJhbWUoc29tX2NsdXN0ZXIpDQpzb21fY2x1c3RlcktleSR1bml0X2NsYXNzaWYgPC0gYygxOmdyaWRTcXVhcmUpDQpkYXRhX3ZhbCA8LSBjYmluZChkYXRhX3ZhbCxzb21fbW9kZWwkdW5pdC5jbGFzc2lmLHNvbV9tb2RlbCRkaXN0YW5jZXMpICU+JSByZW5hbWUodW5pdF9jbGFzc2lmID0gJ3NvbV9tb2RlbCR1bml0LmNsYXNzaWYnLCBkaXN0YW5jZXMgPSAnc29tX21vZGVsJGRpc3RhbmNlcycpDQpkYXRhX3ZhbCA8LSBtZXJnZShkYXRhX3ZhbCwgc29tX2NsdXN0ZXJLZXksIGJ5LnggPSAidW5pdF9jbGFzc2lmIiApDQpoZWFkKGRhdGFfdmFsKQ0KDQojIHBsb3QgcGNhIHdpdGggY29sb3JlZCBjbHVzdGVycw0KcGNhc29tX3Bsb3QgPC0gZ2dwbG90KGRhdGFfdmFsLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBmYWN0b3Ioc29tX2NsdXN0ZXIpLCB0ZXh0ID0gcG9seW1lcl9wcm9kdWNlcikpICsNCiAgICBnZW9tX3J1ZyhhbHBoYSA9IDAuNSkgKyAjIHR3byAxZCBtYXJnaW5hbCBkaXN0cmlidXRpb25zLCBkaXNwbGF5IGluZGl2aWR1YWwgY2FzZXMgc28gYXJlIGJlc3QgdXNlZCB3aXRoIHNtYWxsZXIgZGF0YXNldHMNCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpDQpwY2Fzb21fcGxvdCA8LSBnZ3Bsb3RseShwY2Fzb21fcGxvdCkgJT4lIGxheW91dCgpDQoNCnBjYXNvbV9wbG90DQpgYGANCg0KYGBge3J9DQojIHR3byB2YXJpYWJsZXMsIGNvbnRpbnVvdXMgeCwgY29udGludW91cyB5LCBzaG93IHRyZW5kIGFuZCBkaXN0cmlidXRpb24NCm5hbWUgPSBjKCdwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzJywgJ3RvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUnKQ0KZGYgPC0gbWVyZ2UocGxhc3RpYywgZGF0YV92YWwsIGJ5LnggPSAncG9seW1lcl9wcm9kdWNlcicpDQpkZiA8LSBkZiAlPiUgcmVuYW1lKHggPSBwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzLCB5ID0gdG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgY2x1c3RlciA9IHNvbV9jbHVzdGVyLCB0ZXh0ID0gcG9seW1lcl9wcm9kdWNlcikgJT4lIHNlbGVjdCh4LCB5LCBjbHVzdGVyLCB0ZXh0KSANCg0KIyBodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9zbW9vdGguaHRtbA0KcG9pbnRfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgICMgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHNpemUgPSAxKSArDQogICAgZ2VvbV9ydWcoYWxwaGEgPSAwLjUpICsgIyB0d28gMWQgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucywgZGlzcGxheSBpbmRpdmlkdWFsIGNhc2VzIHNvIGFyZSBiZXN0IHVzZWQgd2l0aCBzbWFsbGVyIGRhdGFzZXRzDQogICAgZ2VvbV9kZW5zaXR5XzJkKGFscGhhID0gMC4yLCBiaW5zID0gNCkgKyMgMkQga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiB1c2luZyBNQVNTOjprZGUyZCgpIGFuZCBkaXNwbGF5IHRoZSByZXN1bHRzIHdpdGggY29udG91cnMNCiAgICBnZW9tX3Ntb290aChmaWxsID0gImdyZXk5MCIpICsgIyBhaWRzIHRoZSBleWUgaW4gc2VlaW5nIHBhdHRlcm5zIGluIHRoZSBwcmVzZW5jZSBvZiBvdmVycGxvdHRpbmcNCiAgICBnZW9tX3BvaW50KGFlcyh0ZXh0ID0gdGV4dCksIGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBnZ3RpdGxlKHBhc3RlKCJ0cmVuZCBvZiIsIG5hbWVbMl0sICJvdmVyIiwgbmFtZVsxXSwgc2VwPSIgIikpIA0KcG9pbnRfcGxvdCA8LSBnZ3Bsb3RseShwb2ludF9wbG90KSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdChzaG93dGlja2xhYmVscyA9IEZBTFNFKSkNCg0KeF9kZW5zaXR5X3Bsb3QgPC0gZGYgJT4lDQogIGdncGxvdChhZXMoeCA9IHgsIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIHN0YXRfZGVuc2l0eShnZW9tPSJsaW5lIikgKyAjIGRyYXdzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlLCB3aGljaCBpcyBhIHNtb290aGVkIHZlcnNpb24gb2YgdGhlIGhpc3RvZ3JhbQ0KICAgICMgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeF9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeF9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCnlfZGVuc2l0eV9wbG90IDwtIGRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB5LCBjb2xvciA9IGZhY3RvcihjbHVzdGVyKSkpICsNCiAgICBzdGF0X2RlbnNpdHkoZ2VvbT0ibGluZSIpICsgIyBkcmF3cyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZSwgd2hpY2ggaXMgYSBzbW9vdGhlZCB2ZXJzaW9uIG9mIHRoZSBoaXN0b2dyYW0NCiAgICAjIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKw0KICAgIGNvb3JkX2ZsaXAoKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeV9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeV9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fcXVhbnRpbGUuaHRtbA0KcXVhbHRpbGVfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIGdlb21fcXVhbnRpbGUoYWxwaGEgPSAwLjgpICsgIyBmaXRzIGEgcXVhbnRpbGUgcmVncmVzc2lvbiB0byB0aGUgZGF0YSBhbmQgZHJhd3MgdGhlIGZpdHRlZCBxdWFudGlsZXMgd2l0aCBsaW5lcw0KICAgIHRoZW1lX21pbmltYWwoKSANCnF1YWx0aWxlX3Bsb3QgPC0gZ2dwbG90bHkocXVhbHRpbGVfcGxvdCkgJT4lIGxheW91dCh5YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgbWVyZ2UgZmlndXJlcyBpbnRvIG9uZSBwbG90LCB2aWEgc3VicGxvdHMsIGh0dHBzOi8vcGxvdGx5LXIuY29tL2FycmFuZ2luZy12aWV3cy5odG1sDQpzdWIxIDwtIHN1YnBsb3QoeF9kZW5zaXR5X3Bsb3QsIHBsb3RseV9lbXB0eSgpLCBwb2ludF9wbG90LCB5X2RlbnNpdHlfcGxvdCwgbnJvd3MgPSAyLCBtYXJnaW4gPSAwLCBoZWlnaHRzID0gYygwLjE1LCAwLjg1KSwgd2lkdGhzID0gYygwLjksIDAuMSksIHNoYXJlWCA9IFRSVUUsIHNoYXJlWSA9IFRSVUUsIHRpdGxlWCA9IEZBTFNFLCB0aXRsZVkgPSBGQUxTRSkgJT4lIGxheW91dCgpDQpzdWIyIDwtIHN1YnBsb3QocXVhbHRpbGVfcGxvdCwgcGxvdGx5X2VtcHR5KCksIG1hcmdpbiA9IDAsIHdpZHRocyA9IGMoMC45LCAwLjEwKSwgdGl0bGVYID0gRkFMU0UsIHRpdGxlWSA9IEZBTFNFKSAlPiUgbGF5b3V0KCkNCmZpZyA8LSBzdWJwbG90KHN1YjEsIHN1YjIsIG5yb3dzID0gMiwgbWFyZ2luID0gMCwgaGVpZ2h0cyA9IGMoMC44LCAwLjIpLCBzaGFyZVggPSBUUlVFKSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9IG5hbWVbMV0pLCB5YXhpcyA9IGxpc3QodGl0bGUgPSBuYW1lWzJdKSkNCiAgDQpmaWcNCmBgYA0KDQotLS0NCnBhcmFsbGVsIGNvb3JkaW5hdGUgcGxvdA0KLS0tDQpwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QgaXMgbm90IHZlcnkgaW5zaWdodGZ1bCwgc2luY2UgdGhlcmUgaXMgc28gbXVjaCBkYXRhIGNsb3NlIHRvZ2V0aGVyLCB3ZSBjYW5ub3Qgc2VlIGFueSBncm91cGluZyB3aXRob3V0IHByaW9yIGtub3dsZWRnZSwgb25seSBoaW50IGlzIHdoZW4geW91IHpvb20gaW4gYXQgdGhlIGJ1bGsgb2YgdGhlIGRhdGEgaW4gdGhlIGxvd2VyIHRoaXJkICh5IGRpcmVjdGlvbiksIHRoZXJlIGlzIGEgbmVnYXRpdmUgY29ycmVsYXRpb24NCg0KYGBge3J9DQpuYW1lID0gYygncGxhc3RpYyBwcm9kdWNlcnMgY2x1c3RlcmVkIGJ5IGZvY3VzJykNCmRmIDwtIG1lcmdlKHBsYXN0aWMsIHNlbGVjdChkYXRhX3ZhbCwgc29tX2NsdXN0ZXIsIHBvbHltZXJfcHJvZHVjZXIpLCBieS54ID0gJ3BvbHltZXJfcHJvZHVjZXInKSAlPiUNCiAgbXV0YXRlKHNvbV9jbHVzdGVyID0gYXMuY2hhcmFjdGVyKHNvbV9jbHVzdGVyKSkgJT4lDQogIHJlbmFtZShwcm9kdWN0ID0gcHJvZHVjdGlvbl9vZl9pbl9zY29wZV9wb2x5bWVycywgDQogICAgICAgICAgZmxleGlibGUgPSBmbGV4aWJsZV9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgDQogICAgICAgICAgcmlnaWQgPSByaWdpZF9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwNCiAgICAgICAgICBhc3NldHMgPSAgbm9fb2ZfYXNzZXRzLA0KICAgICAgICAgIGNsdXN0ZXIgPSBzb21fY2x1c3RlcikgJT4lIA0KICBzZWxlY3QoYXNzZXRzLCBwcm9kdWN0LCBmbGV4aWJsZSwgcmlnaWQsIGNsdXN0ZXIpIA0KDQpsaWJyYXJ5KEdHYWxseSkgIyBleHRlbmRzIGdncGxvdDIgYnkgYWRkaW5nIHNldmVyYWwgZnVuY3Rpb25zIHRvIHJlZHVjZSB0aGUgY29tcGxleGl0eSBvZiBjb21iaW5pbmcgZ2VvbXMgd2l0aCB0cmFuc2Zvcm1lZCBkYXRhDQoNCiMgaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS9wYXJhbGxlbC1wbG90LWdnYWxseS5odG1sI2N1c3RvbQ0KcGFyY29vcmRfcGxvdCA8LSBnZ3BhcmNvb3JkKGRmLA0KICAgICAgICAgICBjb2x1bW5zID0gMTo0LCBncm91cENvbHVtbiA9IDUsDQogICAgICAgICAgIHNjYWxlPSdjZW50ZXInLCAjIHNjYWxpbmc6IHN0YW5kYXJkaXplIGFuZCBjZW50ZXIgdmFyaWFibGVzDQogICAgICAgICAgIHNob3dQb2ludHMgPSBUUlVFLA0KICAgICAgICAgICB0aXRsZSA9IG5hbWUsDQogICAgICAgICAgIGFscGhhTGluZXMgPSAwLjMpICsNCiAgdGhlbWVfbWluaW1hbCgpIA0KcGFyY29vcmRfcGxvdCA8LSBnZ3Bsb3RseShwYXJjb29yZF9wbG90KSAlPiUgbGF5b3V0KGF1dG9zaXplPVQpDQoNCnBhcmNvb3JkX3Bsb3QNCmBgYA0KDQotLS0NCmstbWVhbnMgY2x1c3RlcmluZw0KLS0tDQpodHRwczovL3N0YXRzYW5kci5jb20vYmxvZy9jbHVzdGVyaW5nLWFuYWx5c2lzLWstbWVhbnMtYW5kLWhpZXJhcmNoaWNhbC1jbHVzdGVyaW5nLWJ5LWhhbmQtYW5kLWluLXIvI29wdGltYWwtbnVtYmVyLW9mLWNsdXN0ZXJzDQpBcyBhIHJlbWluZGVyLCB0aGlzIG1ldGhvZCBhaW1zIGF0IHBhcnRpdGlvbmluZyBuIG9ic2VydmF0aW9ucyBpbnRvIGsgY2x1c3RlcnMgaW4gd2hpY2ggZWFjaCBvYnNlcnZhdGlvbiBiZWxvbmdzIHRvIHRoZSBjbHVzdGVyIHdpdGggdGhlIGNsb3Nlc3QgYXZlcmFnZSwgc2VydmluZyBhcyBhIHByb3RvdHlwZSBvZiB0aGUgY2x1c3Rlcg0Kd29ya3Mgbm90IHF1aXRlIGFzIHdlbGwgYXMgU09NLCBidXQgdmVyeSBjbG9zZSwgYWxzbyByZWNvbW1lbmQgY2x1c3RlciBpcyB0aHJlZSwgYnV0IGNsb3NlbHkgZm9sbG93ZWQgYnkgMiwgc2luY2UgMyBkb2VzIG5vdCByZXZlYWwgYW55IHNpZ25pZmljYW50IGNvbm5lY3Rpb24gcmlnaHQgbm93LCBJIHdvbmRlciB3aGF0IHRoYXQgc2hvd3MNCg0KYGBge3J9DQpuYW1lID0gYygncG9seW1lcl9wcm9kdWNlcicpDQpkZiA8LSBwbGFzdGljICU+JSBzZWxlY3QoLSB0b3RhbF93YXN0ZV9kaXZfcHJvZHVjdGlvbiwgLXJhbmssIC1ub19vZl9hc3NldHMsIC10b3RhbF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKSAlPiUgIyByZW1vdmVkIHZhcmlhYmxlcyB3aGljaCBhcmUgZGVwZW5kZWQgb24gZWFjaCBvdGhlcg0KICByZW5hbWUoIHByb2R1Y3QgPSBwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzLCANCiAgICAgICAgICBmbGV4aWJsZSA9IGZsZXhpYmxlX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlLCANCiAgICAgICAgICByaWdpZCA9IHJpZ2lkX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKQ0KDQpsaWJyYXJ5KE5iQ2x1c3QpICMgZGV0ZXJtaW5pbmcgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGluIGEgZGF0YSBzZXQNCmxpYnJhcnkoY2x1c3RlcikgIyBjb21wdXRlcyBhZ2dsb21lcmF0aXZlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG9mIHRoZSBkYXRhc2V0DQoNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgodChzY2FsZSh0KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkpKQ0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChzY2FsZShkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQpzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgIyB3aXRob3V0IHNjYWxpbmcgc2luY2UgdGhlIHZhcmlhYmxlcyBhY3R1YWxseSBhcmUgb24gdGhlIHNhbWUgc2NhbGUNCmhlYWQoc2NhbGVfZGF0YSkNCg0Ka21lYW5zX21vZGVsIDwtIGttZWFucyhzY2FsZV9kYXRhLCBjZW50ZXJzID0gMiwgbnN0YXJ0ID0gMTIpICMgIGstbWVhbnMgY2x1c3RlcmluZyBpcyBkb25lIHZpYSB0aGUga21lYW5zKCkgZnVuY3Rpb24sIHdpdGggdGhlIGFyZ3VtZW50IGNlbnRlcnMgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgbnVtYmVyIG9mIGRlc2lyZWQgY2x1c3RlcnMNCmRmX2NsdXN0ZXIgPC0gdGliYmxlKGRmLCBjbHVzdGVyID0gYXMuZmFjdG9yKGttZWFuc19tb2RlbCRjbHVzdGVyKSkgIyBzdG9yZSBjbHVzdGVyIGluIG9yaWdpbmFsIGRhdGEgc2V0IGFzIGNvbHVtbg0KaGVhZChkZl9jbHVzdGVyKQ0KDQojIGNoZWNrIHF1YWxpdHkgb2YgYSBrLW1lYW5zIHBhcnRpdGlvbg0KcXVhbGl0eSA8LSBrbWVhbnNfbW9kZWwkYmV0d2VlbnNzIC8ga21lYW5zX21vZGVsJHRvdHNzIA0KcHJpbnQocGFzdGUoInF1YWxpdHkgb2Yga21lYW5zIGlzIEJTUy9UU1M6ICIsIGZvcm1hdChyb3VuZChxdWFsaXR5LDIpLCBuc21hbGwgPSAyKSkpDQoNCiMgZmluZCBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KHNjYWxlX2RhdGEsIGttZWFucywgbWV0aG9kID0gJ3dzcycpICsgIyBFbGJvdyBtZXRob2QsIG5lZWRzIHNjYWxlZCBkYXRhDQogICMgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMiwgbGluZXR5cGUgPSAyKSArICMgYWRkIGxpbmUgZm9yIGJldHRlciB2aXN1YWxpemF0aW9uDQogIGxhYnMoc3VidGl0bGUgPSAiRWxib3cgbWV0aG9kIikgIyBhZGQgc3VidGl0bGUNCg0KZnZpel9uYmNsdXN0KHNjYWxlX2RhdGEsIGttZWFucywgbWV0aG9kID0gJ3NpbGhvdWV0dGUnKSArICMgU2lsaG91ZXR0ZSBtZXRob2QsIG5lZWRzIHNjYWxlZCBkYXRhDQogIGxhYnMoc3VidGl0bGUgPSAiU2lsaG91ZXR0ZSBtZXRob2QiKSAjIGFkZCBzdWJ0aXRsZQ0KDQpmdml6X25iY2x1c3QoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0sIGttZWFucywgIyBHYXAgc3RhdGlzdGljcywgbmVlZHMgb3JpZ2luYWwgZGF0YSA/DQogICAgICAgICAgICAgbnN0YXJ0ID0gMjUsDQogICAgICAgICAgICAgbWV0aG9kID0gJ2dhcF9zdGF0JywNCiAgICAgICAgICAgICBuYm9vdCA9IDEwMCkgKyAjIHJlZHVjZSBpdCBmb3IgbG93ZXIgY29tcHV0YXRpb24gdGltZSwgYnV0IGxlc3MgcHJlY2lzZSByZXN1bHRzDQogIGxhYnMoc3VidGl0bGUgPSAiR2FwIHN0YXRpc3RpY3MgbWV0aG9kIikNCg0KbmJjbHVzdF9vdXQgPC0gTmJDbHVzdChkYXRhID0gZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0sICMgTmJDbHVzdCBuZWVkcyB0aGUgb3JpZ2luYWwgZGF0YSA/DQogICAgICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlID0gJ2V1Y2xpZGVhbicsDQogICAgICAgICAgICAgICAgICAgICAgIG1pbi5uYyA9IDIsICMgbWluaW11bSBudW1iZXIgb2YgY2x1c3RlcnMNCiAgICAgICAgICAgICAgICAgICAgICAgbWF4Lm5jID0gMTAsICMgbWF4aW11bSBudW1iZXIgb2YgY2x1c3Rlcg0KICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAnY29tcGxldGUnLA0KICAgICAgICAgICAgICAgICAgICAgICBpbmRleCA9ICdhbGwnKQ0KZnZpel9uYmNsdXN0KG5iY2x1c3Rfb3V0KSArIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoc3VidGl0bGUgPSAiTmJDbHVzdCByZXN1bHRzIikNCg0KIyBjaGVjayBxdWFsaXR5IG9mIGNsdXN0ZXJpbmcNCiMgaWYgYSBsYXJnZSBtYWpvcml0eSBvZiB0aGUgc2lsaG91ZXR0ZSBjb2VmZmljaWVudHMgYXJlIHBvc2l0aXZlLCBpdCBpbmRpY2F0ZXMgdGhhdCB0aGUgb2JzZXJ2YXRpb25zIGFyZSBwbGFjZWQgaW4gdGhlIGNvcnJlY3QgZ3JvdXANCnNpbCA8LSBzaWxob3VldHRlKGttZWFuc19tb2RlbCRjbHVzdGVyLCBkaXN0KHNjYWxlX2RhdGEpKSANCmZ2aXpfc2lsaG91ZXR0ZShzaWwpDQoNCmZ2aXpfY2x1c3RlcihrbWVhbnNfbW9kZWwsIGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdLCBlbGxpcHNlLnR5cGUgPSAnbm9ybScpICsgdGhlbWVfbWluaW1hbCgpDQpmdml6X2NsdXN0ZXIoa21lYW5zX21vZGVsLCBkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgKyB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgodChzY2FsZSh0KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkpKQ0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChzY2FsZShkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQpzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgIyB3aXRob3V0IHNjYWxpbmcgc2luY2UgdGhlIHZhcmlhYmxlcyBhY3R1YWxseSBhcmUgb24gdGhlIHNhbWUgc2NhbGUNCg0Ka21lYW5fY2FsYyA8LSBmdW5jdGlvbihkZiwgLi4uKXsNCiAga21lYW5zKGRmLCBzY2FsZWQgPSAuLi4sIG5zdGFydCA9IDMwKQ0KfQ0KDQprbTIgPC0ga21lYW5fY2FsYyhzY2FsZV9kYXRhLCAyKQ0Ka20zIDwtIGttZWFuX2NhbGMoc2NhbGVfZGF0YSwgMykNCmttNCA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgNCkNCmttNSA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgNSkNCmttNiA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgNikNCmttNyA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgNykNCg0KcDEgPC0gZnZpel9jbHVzdGVyKGttMiwgZGF0YSA9IHNjYWxlX2RhdGEsIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiKSArIHRoZW1lX21pbmltYWwoKQ0KcDEgPC0gZ2dwbG90bHkocDEpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAiayA9IDIiLCBmb250ID0gZiwgeHJlZiA9ICJwYXBlciIsIHlyZWYgPSAicGFwZXIiLCB5YW5jaG9yID0gImJvdHRvbSIsIHhhbmNob3IgPSAiY2VudGVyIiwgYWxpZ24gPSAiY2VudGVyIiwgeCA9IDAuNSwgeSA9IDEsIHNob3dhcnJvdyA9IEZBTFNFKSkNCnAyIDwtIGZ2aXpfY2x1c3RlcihrbTMsIGRhdGEgPSBzY2FsZV9kYXRhLCBlbGxpcHNlLnR5cGUgPSAiY29udmV4IikgKyB0aGVtZV9taW5pbWFsKCkNCnAyIDwtIGdncGxvdGx5KHAyKSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gImsgPSAzIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQpwMyA8LSBmdml6X2NsdXN0ZXIoa200LCBkYXRhID0gc2NhbGVfZGF0YSwgZWxsaXBzZS50eXBlID0gImNvbnZleCIpICsgdGhlbWVfbWluaW1hbCgpDQpwMyA8LSBnZ3Bsb3RseShwMykgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJrID0gNCIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KcDQgPC0gZnZpel9jbHVzdGVyKGttNSwgZGF0YSA9IHNjYWxlX2RhdGEsIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiKSArIHRoZW1lX21pbmltYWwoKQ0KcDQgPC0gZ2dwbG90bHkocDQpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAiayA9IDUiLCBmb250ID0gZiwgeHJlZiA9ICJwYXBlciIsIHlyZWYgPSAicGFwZXIiLCB5YW5jaG9yID0gImJvdHRvbSIsIHhhbmNob3IgPSAiY2VudGVyIiwgYWxpZ24gPSAiY2VudGVyIiwgeCA9IDAuNSwgeSA9IDEsIHNob3dhcnJvdyA9IEZBTFNFKSkNCnA1IDwtIGZ2aXpfY2x1c3RlcihrbTYsIGRhdGEgPSBzY2FsZV9kYXRhLCBlbGxpcHNlLnR5cGUgPSAiY29udmV4IikgKyB0aGVtZV9taW5pbWFsKCkNCnA1IDwtIGdncGxvdGx5KHA1KSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gImsgPSA2IiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQpwNiA8LSBmdml6X2NsdXN0ZXIoa203LCBkYXRhID0gc2NhbGVfZGF0YSwgZWxsaXBzZS50eXBlID0gImNvbnZleCIpICsgdGhlbWVfbWluaW1hbCgpDQpwNiA8LSBnZ3Bsb3RseShwNikgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJrID0gNyIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpmaWcgPC0gc3VicGxvdChwMSwgcDIsIHAzICwgcDQsIHA1LCBwNiwgbnJvd3MgPSAyLCBzaGFyZVggPSBUUlVFLCBzaGFyZVkgPSBUUlVFKSAlPiUgbGF5b3V0KCkgIyBUT09EOiBtYWtlIGFsbCBwbG90cyBsaW5rZWQNCmZpZw0KYGBgDQoNCg0KYGBge3J9DQojIHR3byB2YXJpYWJsZXMsIGNvbnRpbnVvdXMgeCwgY29udGludW91cyB5LCBzaG93IHRyZW5kIGFuZCBkaXN0cmlidXRpb24NCm5hbWUgPSBjKCdwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzJywgJ3RvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUnKQ0KZGYgPC0gdGliYmxlKHBsYXN0aWMsIGNsdXN0ZXIgPSBhcy5mYWN0b3Ioa21lYW5zX21vZGVsJGNsdXN0ZXIpKQ0KZGYgPC0gZGYgJT4lIHJlbmFtZSh4ID0gcHJvZHVjdGlvbl9vZl9pbl9zY29wZV9wb2x5bWVycywgeSA9IHRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIGNsdXN0ZXIgPSBjbHVzdGVyLCB0ZXh0ID0gcG9seW1lcl9wcm9kdWNlcikgJT4lIHNlbGVjdCh4LCB5LCBjbHVzdGVyLCB0ZXh0KSANCg0KIyBodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9zbW9vdGguaHRtbA0KcG9pbnRfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgICMgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHNpemUgPSAxKSArDQogICAgZ2VvbV9ydWcoYWxwaGEgPSAwLjUpICsgIyB0d28gMWQgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucywgZGlzcGxheSBpbmRpdmlkdWFsIGNhc2VzIHNvIGFyZSBiZXN0IHVzZWQgd2l0aCBzbWFsbGVyIGRhdGFzZXRzDQogICAgZ2VvbV9kZW5zaXR5XzJkKGFscGhhID0gMC4yLCBiaW5zID0gNCkgKyMgMkQga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiB1c2luZyBNQVNTOjprZGUyZCgpIGFuZCBkaXNwbGF5IHRoZSByZXN1bHRzIHdpdGggY29udG91cnMNCiAgICBnZW9tX3Ntb290aChmaWxsID0gImdyZXk5MCIpICsgIyBhaWRzIHRoZSBleWUgaW4gc2VlaW5nIHBhdHRlcm5zIGluIHRoZSBwcmVzZW5jZSBvZiBvdmVycGxvdHRpbmcNCiAgICBnZW9tX3BvaW50KGFlcyh0ZXh0ID0gdGV4dCksIGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBnZ3RpdGxlKHBhc3RlKCJ0cmVuZCBvZiIsIG5hbWVbMl0sICJvdmVyIiwgbmFtZVsxXSwgc2VwPSIgIikpIA0KcG9pbnRfcGxvdCA8LSBnZ3Bsb3RseShwb2ludF9wbG90KSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdChzaG93dGlja2xhYmVscyA9IEZBTFNFKSkNCg0KeF9kZW5zaXR5X3Bsb3QgPC0gZGYgJT4lDQogIGdncGxvdChhZXMoeCA9IHgsIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIHN0YXRfZGVuc2l0eShnZW9tPSJsaW5lIikgKyAjIGRyYXdzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlLCB3aGljaCBpcyBhIHNtb290aGVkIHZlcnNpb24gb2YgdGhlIGhpc3RvZ3JhbQ0KICAgICMgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeF9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeF9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCnlfZGVuc2l0eV9wbG90IDwtIGRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB5LCBjb2xvciA9IGZhY3RvcihjbHVzdGVyKSkpICsNCiAgICBzdGF0X2RlbnNpdHkoZ2VvbT0ibGluZSIpICsgIyBkcmF3cyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZSwgd2hpY2ggaXMgYSBzbW9vdGhlZCB2ZXJzaW9uIG9mIHRoZSBoaXN0b2dyYW0NCiAgICAjIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKw0KICAgIGNvb3JkX2ZsaXAoKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeV9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeV9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fcXVhbnRpbGUuaHRtbA0KcXVhbHRpbGVfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIGdlb21fcXVhbnRpbGUoYWxwaGEgPSAwLjgpICsgIyBmaXRzIGEgcXVhbnRpbGUgcmVncmVzc2lvbiB0byB0aGUgZGF0YSBhbmQgZHJhd3MgdGhlIGZpdHRlZCBxdWFudGlsZXMgd2l0aCBsaW5lcw0KICAgIHRoZW1lX21pbmltYWwoKSANCnF1YWx0aWxlX3Bsb3QgPC0gZ2dwbG90bHkocXVhbHRpbGVfcGxvdCkgJT4lIGxheW91dCh5YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgbWVyZ2UgZmlndXJlcyBpbnRvIG9uZSBwbG90LCB2aWEgc3VicGxvdHMsIGh0dHBzOi8vcGxvdGx5LXIuY29tL2FycmFuZ2luZy12aWV3cy5odG1sDQpzdWIxIDwtIHN1YnBsb3QoeF9kZW5zaXR5X3Bsb3QsIHBsb3RseV9lbXB0eSgpLCBwb2ludF9wbG90LCB5X2RlbnNpdHlfcGxvdCwgbnJvd3MgPSAyLCBtYXJnaW4gPSAwLCBoZWlnaHRzID0gYygwLjE1LCAwLjg1KSwgd2lkdGhzID0gYygwLjksIDAuMSksIHNoYXJlWCA9IFRSVUUsIHNoYXJlWSA9IFRSVUUsIHRpdGxlWCA9IEZBTFNFLCB0aXRsZVkgPSBGQUxTRSkgJT4lIGxheW91dCgpDQpzdWIyIDwtIHN1YnBsb3QocXVhbHRpbGVfcGxvdCwgcGxvdGx5X2VtcHR5KCksIG1hcmdpbiA9IDAsIHdpZHRocyA9IGMoMC45LCAwLjEwKSwgdGl0bGVYID0gRkFMU0UsIHRpdGxlWSA9IEZBTFNFKSAlPiUgbGF5b3V0KCkNCmZpZyA8LSBzdWJwbG90KHN1YjEsIHN1YjIsIG5yb3dzID0gMiwgbWFyZ2luID0gMCwgaGVpZ2h0cyA9IGMoMC44LCAwLjIpLCBzaGFyZVggPSBUUlVFKSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9IG5hbWVbMV0pLCB5YXhpcyA9IGxpc3QodGl0bGUgPSBuYW1lWzJdKSkNCg0KZmlnDQpgYGANCg0KLS0tDQpoaWVyYXJjaGljYWwgY2x1c3RlcmluZw0KLS0tDQpodHRwczovL3N0YXRzYW5kci5jb20vYmxvZy9jbHVzdGVyaW5nLWFuYWx5c2lzLWstbWVhbnMtYW5kLWhpZXJhcmNoaWNhbC1jbHVzdGVyaW5nLWJ5LWhhbmQtYW5kLWluLXIvI29wdGltYWwtbnVtYmVyLW9mLWNsdXN0ZXJzDQpyZW1pbmQgdGhhdCB0aGUgZGlmZmVyZW5jZSB3aXRoIHRoZSBwYXJ0aXRpb24gYnkgay1tZWFucyBpcyB0aGF0IGZvciBoaWVyYXJjaGljYWwgY2x1c3RlcmluZywgdGhlIG51bWJlciBvZiBjbGFzc2VzIGlzIG5vdCBzcGVjaWZpZWQgaW4gYWR2YW5jZQ0KDQppdCBzZWVtcyB0aGF0IG1vc3QgY2x1c3RlcnMgYXJlIGNvbmZ1c2VkIGJ5IHNvbWUgc3BlY2lmaWMgdmFsdWVzICg4MSwgNjMsIDcyLCAzNj8pLCBhcmUgdGhleSBzb21lIHNvcnQgb2Ygb3V0bGllciwgb3Igd2hhdCBpcyBzcGVjaWFsIGFib3V0IHRoZW0/DQoNCmBgYHtyfQ0KbmFtZSA9IGMoJ3BvbHltZXJfcHJvZHVjZXInKQ0KZGYgPC0gcGxhc3RpYyAlPiUgc2VsZWN0KC0gdG90YWxfd2FzdGVfZGl2X3Byb2R1Y3Rpb24sIC1yYW5rLCAtbm9fb2ZfYXNzZXRzLCAtdG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSkgJT4lICMgcmVtb3ZlZCB2YXJpYWJsZXMgd2hpY2ggYXJlIGRlcGVuZGVkIG9uIGVhY2ggb3RoZXINCiAgcmVuYW1lKCBwcm9kdWN0ID0gcHJvZHVjdGlvbl9vZl9pbl9zY29wZV9wb2x5bWVycywgDQogICAgICAgICAgZmxleGlibGUgPSBmbGV4aWJsZV9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgDQogICAgICAgICAgcmlnaWQgPSByaWdpZF9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSkNCg0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeCh0KHNjYWxlKHQoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKSkpDQojIHNjYWxlX2RhdGEgPC0gYXMubWF0cml4KHNjYWxlKGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkNCnNjYWxlX2RhdGEgPC0gYXMubWF0cml4KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSAjIHdpdGhvdXQgc2NhbGluZyBzaW5jZSB0aGUgdmFyaWFibGVzIGFjdHVhbGx5IGFyZSBvbiB0aGUgc2FtZSBzY2FsZQ0KaGVhZChzY2FsZV9kYXRhKQ0KDQpub19rID0gMjsNCg0KIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZzogc2luZ2xlIGxpbmthZ2UNCmhjbHVzdF9yZXMgPC0gaGNsdXN0KGRpc3Qoc2NhbGVfZGF0YSksIG1ldGhvZCA9ICdzaW5nbGUnKQ0KZnZpel9kZW5kKGhjbHVzdF9yZXMsIGsgPSBub19rLCByZWN0ID0gVFJVRSkNCg0KIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZzogY29tcGxldGUgbGlua2FnZQ0KaGNsdXN0X3JlcyA8LSBoY2x1c3QoZGlzdChzY2FsZV9kYXRhKSwgbWV0aG9kID0gJ2NvbXBsZXRlJykNCnBsb3QoaGNsdXN0X3JlcykNCnJlY3QuaGNsdXN0KGhjbHVzdF9yZXMsIGsgPSBub19rLCBib3JkZXIgPSAnYmx1ZScpDQoNCiMgSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmc6IGF2ZXJhZ2UgbGlua2FnZQ0KaGNsdXN0X3JlcyA8LSBoY2x1c3QoZGlzdChzY2FsZV9kYXRhKSwgbWV0aG9kID0gJ2F2ZXJhZ2UnKQ0KcGxvdChoY2x1c3RfcmVzKQ0KcmVjdC5oY2x1c3QoaGNsdXN0X3JlcywgayA9IG5vX2ssIGJvcmRlciA9ICdibHVlJykNCg0KIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZzogd2FyZA0KaGNsdXN0X3JlcyA8LSBoY2x1c3QoZGlzdChzY2FsZV9kYXRhKSwgbWV0aG9kID0gJ3dhcmQuRDInKQ0KZnZpel9kZW5kKGhjbHVzdF9yZXMsIGsgPSBub19rLCByZWN0ID0gVFJVRSkNCg0KIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZzogbWNxdWl0dHkNCmhjbHVzdF9yZXMgPC0gaGNsdXN0KGRpc3Qoc2NhbGVfZGF0YSksIG1ldGhvZCA9ICdtY3F1aXR0eScpDQpwbG90KGhjbHVzdF9yZXMpDQpyZWN0LmhjbHVzdChoY2x1c3RfcmVzLCBrID0gbm9faywgYm9yZGVyID0gJ2JsdWUnKQ0KDQojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nOiBjZW50cm9pZA0KaGNsdXN0X3JlcyA8LSBoY2x1c3QoZGlzdChzY2FsZV9kYXRhKSwgbWV0aG9kID0gJ2NlbnRyb2lkJykNCnBsb3QoaGNsdXN0X3JlcykNCnJlY3QuaGNsdXN0KGhjbHVzdF9yZXMsIGsgPSBub19rLCBib3JkZXIgPSAnYmx1ZScpDQpgYGANCg0KLS0tDQpDbHVzdHJlZQ0KLS0tDQpodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vMTAtdGlwcy1mb3ItY2hvb3NpbmctdGhlLW9wdGltYWwtbnVtYmVyLW9mLWNsdXN0ZXJzLTI3N2U5M2Q3MmQ5Mg0KSW4gdGhpcyBmaWd1cmUgdGhlIHNpemUgb2YgZWFjaCBub2RlIGNvcnJlc3BvbmRzIHRvIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyBpbiBlYWNoIGNsdXN0ZXIsIGFuZCB0aGUgYXJyb3dzIGFyZSBjb2xvdXJlZCBhY2NvcmRpbmcgdG8gdGhlIG51bWJlciBvZiBzYW1wbGVzIGVhY2ggY2x1c3RlciByZWNlaXZlcy4gQSBzZXBhcmF0ZSBzZXQgb2YgYXJyb3dzLCB0aGUgdHJhbnNwYXJlbnQgb25lcywgY2FsbGVkIHRoZSBpbmNvbWluZyBub2RlIHByb3BvcnRpb24sIGFyZSBhbHNvIGNvbG91cmVkIGFuZCBzaG93cyBob3cgc2FtcGxlcyBmcm9tIG9uZSBncm91cCBlbmQgdXAgaW4gYW5vdGhlciBncm91cCDigJQgYW4gaW5kaWNhdG9yIG9mIGNsdXN0ZXIgaW5zdGFiaWxpdHkuDQoNCmBgYHtyLCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDl9DQpuYW1lID0gYygncG9seW1lcl9wcm9kdWNlcicpDQpkZiA8LSBwbGFzdGljICU+JSBzZWxlY3QoLSB0b3RhbF93YXN0ZV9kaXZfcHJvZHVjdGlvbiwgLXJhbmssIC1ub19vZl9hc3NldHMsIC10b3RhbF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKSAlPiUgIyByZW1vdmVkIHZhcmlhYmxlcyB3aGljaCBhcmUgZGVwZW5kZWQgb24gZWFjaCBvdGhlcg0KICByZW5hbWUoIHByb2R1Y3QgPSBwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzLCANCiAgICAgICAgICBmbGV4aWJsZSA9IGZsZXhpYmxlX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlLCANCiAgICAgICAgICByaWdpZCA9IHJpZ2lkX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKQ0KDQojIGNvbmZpZw0Kbm9fb2ZfY2x1c3RlciA9IDkNCg0KbGlicmFyeShjbHVzdHJlZSkgIyBwcm9kdWNlIGNsdXN0ZXJpbmcgdHJlZXMsIGEgdmlzdWFsaXphdGlvbiBmb3IgaW50ZXJyb2dhdGluZyBjbHVzdGVyaW5ncyBhcyByZXNvbHV0aW9uIGluY3JlYXNlcw0KDQojIHNjYWxlX2RhdGEgPC0gYXMubWF0cml4KHQoc2NhbGUodChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpKSkNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgoc2NhbGUoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKQ0Kc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pICMgd2l0aG91dCBzY2FsaW5nIHNpbmNlIHRoZSB2YXJpYWJsZXMgYWN0dWFsbHkgYXJlIG9uIHRoZSBzYW1lIHNjYWxlDQpoZWFkKHNjYWxlX2RhdGEpDQoNCnRtcCA8LSBOVUxMDQpmb3IgKGsgaW4gMTpub19vZl9jbHVzdGVyKXsNCiAgdG1wW2tdIDwtIGttZWFucyhzY2FsZV9kYXRhLCBrLCBuc3RhcnQgPSAzMCkNCn0NCg0KdG1wX2RmIDwtIGRhdGEuZnJhbWUodG1wKQ0KY29sbmFtZXModG1wX2RmKSA8LSBzZXEoMTpub19vZl9jbHVzdGVyKSAjIGFkZCBwcmVmaXggdG8gdGhlIGNvbHVtbiBuYW1lcw0KY29sbmFtZXModG1wX2RmKSA8LSBwYXN0ZTAoImsiLCBjb2xuYW1lcyh0bXBfZGYpKSANCg0KIyBnZXQgaW5kaXZpZHVhbCBQQ0ENCnRtcF9kZi5wY2EgPC0gcHJjb21wKHRtcF9kZiwgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gRkFMU0UpDQoNCmluZC5jb29yZCA8LSB0bXBfZGYucGNhJHgNCmluZC5jb29yZCA8LSBpbmQuY29vcmRbLDE6Ml0NCg0KdG1wX2RmIDwtIGJpbmRfY29scyhhcy5kYXRhLmZyYW1lKHRtcF9kZiksIGFzLmRhdGEuZnJhbWUoaW5kLmNvb3JkKSkNCg0KY2x1c3RyZWUodG1wX2RmLCBwcmVmaXggPSAiayIpICMgcHJvZHVjZSBjbHVzdGVyaW5nIHRyZWVzLCBhIHZpc3VhbGl6YXRpb24gZm9yIGludGVycm9nYXRpbmcgY2x1c3RlcmluZyBhcyByZXNvbHV0aW9uIGluY3JlYXNlcw0KDQpvdmVybGF5X2xpc3QgPC0gY2x1c3RyZWVfb3ZlcmxheSh0bXBfZGYsIHByZWZpeCA9ICJrIiwgeF92YWx1ZSA9ICJQQzEiLCB5X3ZhbHVlID0gIlBDMiIsIHBsb3Rfc2lkZXMgPSBUUlVFKQ0Kb3ZlcmxheV9saXN0JG92ZXJsYXkNCm92ZXJsYXlfbGlzdCR4X3NpZGUNCm92ZXJsYXlfbGlzdCR5X3NpZGUNCmBgYA0KDQotLS0NCmNsVmFsaWQgdG8gY2hvb3NlIGJlc3QgY2x1c3RlcmluZyBhbGdvDQotLS0NCmh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS8xMC10aXBzLWZvci1jaG9vc2luZy10aGUtb3B0aW1hbC1udW1iZXItb2YtY2x1c3RlcnMtMjc3ZTkzZDcyZDkyDQpUaGUgY1ZhbGlkIHBhY2thZ2UgY2FuIGJlIHVzZWQgdG8gc2ltdWx0YW5lb3VzbHkgY29tcGFyZSBtdWx0aXBsZSBjbHVzdGVyaW5nIGFsZ29yaXRobXMsIHRvIGlkZW50aWZ5IHRoZSBiZXN0IGNsdXN0ZXJpbmcgYXBwcm9hY2ggYW5kIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycy4gV2Ugd2lsbCBjb21wYXJlIGstbWVhbnMsIGhpZXJhcmNoaWNhbCBhbmQgUEFNIGNsdXN0ZXJpbmcuDQpDb25uZWN0aXZpdHkgYW5kIFNpbGhvdWV0dGUgYXJlIGJvdGggbWVhc3VyZW1lbnRzIG9mIGNvbm5lY3RlZG5lc3Mgd2hpbGUgdGhlIER1bm4gSW5kZXggaXMgdGhlIHJhdGlvIG9mIHRoZSBzbWFsbGVzdCBkaXN0YW5jZSBiZXR3ZWVuIG9ic2VydmF0aW9ucyBub3QgaW4gdGhlIHNhbWUgY2x1c3RlciB0byB0aGUgbGFyZ2VzdCBpbnRyYS1jbHVzdGVyIGRpc3RhbmNlLg0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCmxpYnJhcnkoY2xWYWxpZCkgIyBTdGF0aXN0aWNhbCBhbmQgYmlvbG9naWNhbCB2YWxpZGF0aW9uIG9mIGNsdXN0ZXJpbmcgcmVzdWx0cy4gVGhpcyBwYWNrYWdlIGltcGxlbWVudHMgRHVubiBJbmRleCwgU2lsaG91ZXR0ZSwgQ29ubmVjdGl2aXR5LCBTdGFiaWxpdHksIEJISSBhbmQgQlNJDQpsaWJyYXJ5KG1jbHVzdCkgIyBCSUMgZm9yIHBhcmFtZXRlcml6ZWQgR2F1c3NpYW4gbWl4dHVyZSBtb2RlbHMgZml0dGVkIGJ5IEVNIGFsZ29yaXRobSBpbml0aWFsaXplZCBieSBtb2RlbC1iYXNlZCBoaWVyYXJjaGljYWwgY2x1c3RlcmluZw0KDQojIHNjYWxlX2RhdGEgPC0gYXMubWF0cml4KHQoc2NhbGUodChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpKSkNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgoc2NhbGUoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKQ0Kc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pICMgd2l0aG91dCBzY2FsaW5nIHNpbmNlIHRoZSB2YXJpYWJsZXMgYWN0dWFsbHkgYXJlIG9uIHRoZSBzYW1lIHNjYWxlDQoNCmludGVybiA8LSBjbFZhbGlkKHNjYWxlX2RhdGEsIG5DbHVzdCA9IDI6OSwgDQogICAgICAgICAgICAgICAgICBjbE1ldGhvZHMgPSBjKCJoaWVyYXJjaGljYWwiLCAia21lYW5zIiwgImRpYW5hIiwgImZhbm55IiwgInNvbSIsICJtb2RlbCIsICJzb3RhIiwgInBhbSIsICJjbGFyYSIsICJhZ25lcyIpLA0KICAgICAgICAgICAgICAgICAgdmFsaWRhdGlvbiA9ICJpbnRlcm5hbCIpDQoNCnN1bW1hcnkoaW50ZXJuKQ0KYGBgDQoNCi0tLQ0KRXh0cmFjdGluZyBGZWF0dXJlcyBvZiBDbHVzdGVycw0KLS0tDQpodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vMTAtdGlwcy1mb3ItY2hvb3NpbmctdGhlLW9wdGltYWwtbnVtYmVyLW9mLWNsdXN0ZXJzLTI3N2U5M2Q3MmQ5Mg0KVWx0aW1hdGVseSwgd2Ugd291bGQgbGlrZSB0byBhbnN3ZXIgcXVlc3Rpb25zIGxpa2Ug4oCcd2hhdCBpcyBpdCB0aGF0IG1ha2VzIHRoaXMgY2x1c3RlciB1bmlxdWUgZnJvbSBvdGhlcnM/4oCdIGFuZCDigJx3aGF0IGFyZSB0aGUgY2x1c3RlcnMgdGhhdCBhcmUgc2ltaWxhciB0byBvbmUgYW5vdGhlcuKAnS4gTGV04oCZcyBzZWxlY3QgZm91ciBjbHVzdGVycyBhbmQgaW50ZXJyb2dhdGUgdGhlIGZlYXR1cmVzIG9mIHRoZXNlIGNsdXN0ZXJzLg0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCm5vX2sgPSAzDQpub192YXIgPSAzDQoNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgodChzY2FsZSh0KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkpKQ0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChzY2FsZShkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQpzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgIyB3aXRob3V0IHNjYWxpbmcgc2luY2UgdGhlIHZhcmlhYmxlcyBhY3R1YWxseSBhcmUgb24gdGhlIHNhbWUgc2NhbGUNCg0KIyBjb21wdXRlIGRpc3NpbWlsYXJpdHkgbWF0cml4IHdpdGggZXVjbGlkZWFuIGRpc3RhbmNlcw0KZCA8LSBkaXN0KHNjYWxlX2RhdGEsIG1ldGhvZCA9ICdldWNsaWRlYW4nKQ0KDQojIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHVzaW5nIFdhcmQncyBtZXRob2QNCnJlc19oYyA8LSBoY2x1c3QoZCwgbWV0aG9kID0gJ3dhcmQuRDInKQ0KDQojIGN1dCB0cmVlIGludG8gMyBncm91cHMNCmdycCA8LSBjdXRyZWUocmVzX2hjLCBrID0gbm9faykNCg0KIyB2aXN1YWxpemUNCnBsb3QocmVzX2hjLCBjZXggPSAwLjYpICMgcGxvdCB0cmVlDQpyZWN0LmhjbHVzdChyZXNfaGMsIGsgPSBub19rLCBib3JkZXIgPSAyOjUpICMgYWRkIHJlY3RhbmdsZXMNCg0KIyBleGVjdXRpb24gb2Ygay1tZWFucyB3aXRoIGsgPSA0DQpmaW5hbCA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgbm9faywgbnN0YXJ0ID0gMzApDQoNCmZ2aXpfY2x1c3RlcihmaW5hbCwgZGF0YSA9IHNjYWxlX2RhdGEpICsgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgiayA9IDQiKQ0KYGBgDQpgYGB7cn0NCmFzLnRpYmJsZShzY2FsZV9kYXRhKSAlPiUgDQogIG11dGF0ZShjbHVzdGVyID0gZmluYWwkY2x1c3RlcikgJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICBzdW1tYXJpc2VfYWxsKCdtZWFuJykNCg0KYXMudGliYmxlKHBsYXN0aWMpICU+JSANCiAgbXV0YXRlKGNsdXN0ZXIgPSBmaW5hbCRjbHVzdGVyKSAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHN1bW1hcmlzZV9hbGwoJ21lYW4nKQ0KYGBgDQpgYGB7cn0NCmRmIDwtIGFzX3RpYmJsZShzY2FsZV9kYXRhKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCkNCg0KY2x1c3Rlcl9wb3MgPC0gYXNfdGliYmxlKGZpbmFsJGNsdXN0ZXIpICU+JSByb3duYW1lc190b19jb2x1bW4oKQ0KY29sbmFtZXMoY2x1c3Rlcl9wb3MpIDwtIGMoInJvd25hbWUiLCAiY2x1c3RlciIpDQoNCmZpbmFsIDwtIGlubmVyX2pvaW4oY2x1c3Rlcl9wb3MsIGRmLCBieSA9ICJyb3duYW1lIikNCg0KbGlicmFyeShnZ2lyYXBoRXh0cmEpICMgZW5oYW5jZSAnZ2dwbG90MicgYW5kICdnZ2lyYXBoJywgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBleHBsb3JhdG9yeSBwbG90cywgc2VlIGh0dHBzOi8vcnB1YnMuY29tL2NhcmRpb21vb24vMjMxODIwDQoNCnJhZGFyIDwtIGdnUmFkYXIoZmluYWxbLTFdLCBhZXMoZ3JvdXAgPSBjbHVzdGVyKSwgcmVzY2FsZSA9IEZBTFNFLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHNpemUgPSAxLCBpbnRlcmFjdGl2ZSA9IEZBTFNFLCB1c2UubGFiZWwgPSBUUlVFKSArDQogIGZhY2V0X3dyYXAofmNsdXN0ZXIpICsNCiAgc2NhbGVfeV9kaXNjcmV0ZShicmVha3MgPSBOVUxMKSArICMgZG9uJ3Qgc2hvdyB0aWNrcw0KICB0aGVtZV9taW5pbWFsKCkNCg0KcmFkYXINCmBgYA0KYGBge3J9DQpkZiA8LSBhc190aWJibGUoc2NhbGVfZGF0YSkNCmRmJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKGZpbmFsJGNsdXN0ZXIpDQoNCmxpYnJhcnkoR0dhbGx5KSAjIGV4dGVuZHMgZ2dwbG90MiBieSBhZGRpbmcgc2V2ZXJhbCBmdW5jdGlvbnMgdG8gcmVkdWNlIHRoZSBjb21wbGV4aXR5IG9mIGNvbWJpbmluZyBnZW9tcyB3aXRoIHRyYW5zZm9ybWVkIGRhdGENCg0KZ2dwYWlycyhkZiwgMTpub192YXIsIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoY29sb3IgPSBjbHVzdGVyLCBhbHBoYSA9IDAuNSksDQogICAgICAgIGRpYWcgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJkZW5zaXR5RGlhZyIpKSwNCiAgICAgICAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJwb2ludHMiLCBhbHBoYSA9IDAuOSkpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpgYGB7cn0NCmRmIDwtIGFzX3RpYmJsZShzY2FsZV9kYXRhKQ0KZGYkY2x1c3RlciA8LSBhcy5mYWN0b3IoZmluYWwkY2x1c3RlcikNCg0KYm94MSA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gY2x1c3RlciwgeSA9IHByb2R1Y3QsIGNvbG91ciA9IGNsdXN0ZXIpKSArIA0KICAgIGdlb21fYm94cGxvdCgpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKyANCiAgICBnZ3RpdGxlKCJwcm9kdWN0IikNCmJveDEgPC0gZ2dwbG90bHkoYm94MSkgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJwcm9kdWN0IiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCmJveDIgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGNsdXN0ZXIsIHkgPSByaWdpZCwgY29sb3VyID0gY2x1c3RlcikpICsgDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgZ2d0aXRsZSgicmlnaWQiKQ0KYm94MiA8LSBnZ3Bsb3RseShib3gyKSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gInJpZ2lkIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCmJveDMgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGNsdXN0ZXIsIHkgPSBmbGV4aWJsZSwgY29sb3VyID0gY2x1c3RlcikpICsgDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgZ2d0aXRsZSgiZmxleGlibGUiKQ0KYm94MyA8LSBnZ3Bsb3RseShib3gzKSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gImZsZXhpYmxlIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCmZpZyA8LSBzdWJwbG90KGJveDEsIGJveDIsIGJveDMsIG1hcmdpbiA9IDApICU+JSBsYXlvdXQoKQ0KDQpmaWcNCmBgYA0KYGBge3J9DQpkZiA8LSBhc190aWJibGUoc2NhbGVfZGF0YSkNCmRmJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKGZpbmFsJGNsdXN0ZXIpDQoNCmxpYnJhcnkoR0dhbGx5KSAjIGV4dGVuZHMgZ2dwbG90MiBieSBhZGRpbmcgc2V2ZXJhbCBmdW5jdGlvbnMgdG8gcmVkdWNlIHRoZSBjb21wbGV4aXR5IG9mIGNvbWJpbmluZyBnZW9tcyB3aXRoIHRyYW5zZm9ybWVkIGRhdGENCg0KIyBodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tL3BhcmFsbGVsLXBsb3QtZ2dhbGx5Lmh0bWwjY3VzdG9tDQpwYXJjb29yZF9wbG90IDwtIGdncGFyY29vcmQoZGYsDQogICAgICAgICAgIGNvbHVtbnMgPSAxOm5vX3ZhciwgZ3JvdXBDb2x1bW4gPSAobm9fdmFyKzEpLA0KICAgICAgICAgICBzY2FsZT0nY2VudGVyJywgIyBzY2FsaW5nOiBzdGFuZGFyZGl6ZSBhbmQgY2VudGVyIHZhcmlhYmxlcw0KICAgICAgICAgICBzaG93UG9pbnRzID0gVFJVRSwNCiAgICAgICAgICAgdGl0bGUgPSBuYW1lLA0KICAgICAgICAgICBhbHBoYUxpbmVzID0gMC4zKSArDQogIHRoZW1lX21pbmltYWwoKSANCnBhcmNvb3JkX3Bsb3QgPC0gZ2dwbG90bHkocGFyY29vcmRfcGxvdCkgJT4lIGxheW91dChhdXRvc2l6ZT1UKQ0KDQpwYXJjb29yZF9wbG90DQpgYGANCg0KLS0tDQpjb21wYXJlIHNjYWxlIGZ1bmN0aW9ucw0KLS0tDQpzY2FsZSBpcyBnZW5lcmljIGZ1bmN0aW9uIHdob3NlIGRlZmF1bHQgbWV0aG9kIGNlbnRlcnMgYW5kL29yIHNjYWxlcyB0aGUgY29sdW1ucyBvZiBhIG51bWVyaWMgbWF0cml4DQpub3JtYWxseSB5b3Ugc2NhbGUgdGhlIGNvbHVtbnMgKGllIHZhcmlhYmxlcykgdG8gaGF2ZSBhbGwgdmFyaWFibGVzIG9uIHRoZSBzYW1lIHNjYWxlIHN0IGFuIGluY3JlYXNlIGluIDEgdW5pdCBoYXMgdGhlIHNhbWUgbWVhbmluZyBhY3Jvc3MgYWxsIHZhcmlhYmxlcywgdGhlIHNoYXBlIC8gZGlzdHJpYnV0aW9uIHdpbGwgbm90IGNoYW5nZSwgYnV0IHRoZSBheGlzIHVuaXRzIChzZWUgc2NhdHRlciBwbG90cykNCnNjYWxpbmcgdGhlIHJvd3MgKGllIG9ic2VydmF0aW9ucykgbWFkZSBhbGwgb2JzZXJ2YXRpb25zIGVxdWFsIGluIHRoZSBzZW5zZSB0aGF0IHRoZSBhYnNvbHV0ZSBkaXN0YW5jZXMgd2VyZSByZXBsYWNlZCBieSByZWxhdGl2ZSBkaXN0YW5jZXMgYWNyb3NzIGFsbCBvYnNlcnZhdGlvbnMsIHN0IG5vdyB0aGUgYmlnZ2VzdCB2YWx1ZSBpbiBhIHJvdyB3YXMgYWNyb3NzIGFsbCByb3dzIGVxdWFsbHkgYmlnIChzZWUgImRhdGFmcmFtZSIgcGxvdCksDQppbiBvdXIgY2FzZSB0aGF0IHdhcyB0aGUgcHJvZHVjdGlvbiB2YXJpYWJsZSwgbm93IHJpZ2lkIGFuZCBmbGV4aWJsZSB3ZXJlIHNlcGFyYXRlZCBpZiB0aGVyZSB3YXMgbW9yZSBvciBsZXNzIG9mIHRoZW0gcmVsYXRpdmUgdG8gdGhlIHRvdGFsIHByb2R1Y3Rpb24gKGJpZ2dlc3QgdmFsdWUsIHdlbGwgYWN0dWFsbHkgdG8gdGhlIG1lYW4gbm90IHRoZSBtYXgsIGJ1dCBuZXZlciBtaW5kKSwgdGhpcyBzb3J0ZWQgYWxsIG9ic2VydmF0aW9ucyBhY2NvcmRpbmcgdG8gYmUgbW9yZSBvbiB0aGUgcmlnaWQgb3IgZmxleGlibGUgc2lkZSwNCmFuZCB0aGF0IHdhcyBleGNhdGx5IHdoYXQgSSB3YXMgbG9va2luZyBmb3IgKGFuZCB3aHkgSSB3YXMgdG9vIGhhcHB5IHRvIHNlZSBteSBtaXN0YWtlIGluIHRoZSBmaXJzdCBwbGFjZSkNCg0KYGBge3J9DQpuYW1lID0gYygncG9seW1lcl9wcm9kdWNlcicpDQpkZiA8LSBwbGFzdGljICU+JSBzZWxlY3QoLSB0b3RhbF93YXN0ZV9kaXZfcHJvZHVjdGlvbiwgLXJhbmssIC1ub19vZl9hc3NldHMsIC10b3RhbF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKSAlPiUgIyByZW1vdmVkIHZhcmlhYmxlcyB3aGljaCBhcmUgZGVwZW5kZWQgb24gZWFjaCBvdGhlcg0KICByZW5hbWUoIHByb2R1Y3QgPSBwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzLCANCiAgICAgICAgICBmbGV4aWJsZSA9IGZsZXhpYmxlX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlLCANCiAgICAgICAgICByaWdpZCA9IHJpZ2lkX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKQ0KDQpzY2FsZV9kYXRhIDwtIGFzX3RpYmJsZSh0KHNjYWxlKHQoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKSkpICMgV2UgbmVlZCB0byBub3JtYWxpemUgdGhlIGRhdGEgYmFzZWQgb24gc2NhbGUgZnVuY3Rpb24gYmVjYXVzZSB0aGUgdmFyaWFibGVzIGFyZSBkaWZmZXJlbnQgc2NhbGVzOyBOb3JtYWxpemF0aW9uIG1lYW5zIHN1YnRyYWN0aW5nIG1lYW4gZnJvbSBlYWNoIG9ic2VydmF0aW9uIGFuZCBkaXZpZGluZyB3aXRoIHN0YW5kYXJkIGRldmlhdGlvbi4gQ2hlY2sgYWxsIHRoZSB2YXJpYWJsZXMgbWVhbiB2YWx1ZXMgYXJlIHplcm8gbm93DQoNCmhlYWQoc2NhbGVfZGF0YSkNCg0Kc2NhbGVfZGF0YSA8LSBhc190aWJibGUoc2NhbGUoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKQ0KDQpoZWFkKHNjYWxlX2RhdGEpDQoNCmhlYWQodChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQoNCmxpYnJhcnkoY2FyZXQpDQpwcmVPYmogPC0gcHJlUHJvY2VzcyhkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSwgbWV0aG9kPWMoImNlbnRlciIsICJzY2FsZSIpKQ0KbmV3RGF0YSA8LSBwcmVkaWN0KHByZU9iaiwgZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pDQpoZWFkKG5ld0RhdGEpDQoNCiMgZmluZCBtb3JlIG1ldGhvZHMgaGVyZTogaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTUyMTU0NTcvc3RhbmRhcmRpemUtZGF0YS1jb2x1bW5zLWluLXINCmBgYA0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCnNjYWxlUm93IDwtIGFzX3RpYmJsZSh0KHNjYWxlKHQoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKSkpDQpoZWFkKHNjYWxlUm93KQ0Kc2NhbGVDb2wgPC0gYXNfdGliYmxlKHNjYWxlKGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkNCmhlYWQoc2NhbGVDb2wpDQpkZiA8LSBkZiAlPiUgc2VsZWN0KC1wb2x5bWVyX3Byb2R1Y2VyKQ0KaGVhZChkZikNCg0Kc2NhdHRlclJvdyA8LSBnZ3Bsb3Qoc2NhbGVSb3csIGFlcyh4ID0gZmxleGlibGUsIHkgPSByaWdpZCkpICsNCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpIA0Kc2NhdHRlclJvdyA8LSBnZ3Bsb3RseShzY2F0dGVyUm93KSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gInNjYWxlUm93IiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCnNjYXR0ZXJDb2wgPC0gZ2dwbG90KHNjYWxlQ29sLCBhZXMoeCA9IGZsZXhpYmxlLCB5ID0gcmlnaWQpKSArDQogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsgIyBwb2ludCBnZW9tIGlzIHVzZWQgdG8gY3JlYXRlIHNjYXR0ZXJwbG90cw0KICAgIHRoZW1lX21pbmltYWwoKSANCnNjYXR0ZXJDb2wgPC0gZ2dwbG90bHkoc2NhdHRlckNvbCkgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJzY2FsZUNvbCIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpzY2F0dGVyRGYgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGZsZXhpYmxlLCB5ID0gcmlnaWQpKSArDQogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsgIyBwb2ludCBnZW9tIGlzIHVzZWQgdG8gY3JlYXRlIHNjYXR0ZXJwbG90cw0KICAgIHRoZW1lX21pbmltYWwoKSANCnNjYXR0ZXJEZiA8LSBnZ3Bsb3RseShzY2F0dGVyRGYpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAibm9ybWFsIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCnN1YnBsb3Qoc2NhdHRlclJvdywgc2NhdHRlckNvbCwgc2NhdHRlckRmKSAlPiUgbGF5b3V0KCkNCg0KDQpzY2FsZVJvdzIgPC0gc2NhbGVSb3cgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gYyhmbGV4aWJsZSxyaWdpZCxwcm9kdWN0KSkNCmJveFJvdyA8LSBnZ3Bsb3Qoc2NhbGVSb3cyLCBhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSwgY29sb3VyID0gbmFtZSkpICsNCiAgICBnZW9tX2JveHBsb3QoKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KYm94Um93IDwtIGdncGxvdGx5KGJveFJvdykgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJzY2FsZVJvdyIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpzY2FsZUNvbDIgPC0gc2NhbGVDb2wgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gYyhmbGV4aWJsZSxyaWdpZCxwcm9kdWN0KSkNCmJveENvbCA8LSBnZ3Bsb3Qoc2NhbGVDb2wyLCBhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSwgY29sb3VyID0gbmFtZSkpICsNCiAgICBnZW9tX2JveHBsb3QoKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KYm94Q29sIDwtIGdncGxvdGx5KGJveENvbCkgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJzY2FsZUNvbCIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpkZl9sb25nIDwtIGRmICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoZmxleGlibGUscmlnaWQscHJvZHVjdCkpDQpib3hEZiA8LSBnZ3Bsb3QoZGZfbG9uZywgYWVzKHggPSBuYW1lLCB5ID0gdmFsdWUsIGNvbG91ciA9IG5hbWUpKSArDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHRoZW1lX21pbmltYWwoKSANCmJveERmIDwtIGdncGxvdGx5KGJveERmKSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gIm5vcm1hbCIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpzdWJwbG90KGJveFJvdywgYm94Q29sLCBib3hEZikgJT4lIGxheW91dCgpDQoNCg0Kc2NhbGVSb3cgPC0gc2NhbGVSb3cgJT4lIHJvd2lkX3RvX2NvbHVtbigpICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoZmxleGlibGUscmlnaWQscHJvZHVjdCkpDQpzY2F0dGVyUm93IDwtIGdncGxvdChzY2FsZVJvdywgYWVzKHggPSB2YWx1ZSwgeSA9IHJvd2lkLCBjb2xvdXIgPSBuYW1lKSkgKw0KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjc1KSArICMgcG9pbnQgZ2VvbSBpcyB1c2VkIHRvIGNyZWF0ZSBzY2F0dGVycGxvdHMNCiAgICB0aGVtZV9taW5pbWFsKCkgDQpzY2F0dGVyUm93IDwtIGdncGxvdGx5KHNjYXR0ZXJSb3cpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAic2NhbGVSb3ciLCBmb250ID0gZiwgeHJlZiA9ICJwYXBlciIsIHlyZWYgPSAicGFwZXIiLCB5YW5jaG9yID0gImJvdHRvbSIsIHhhbmNob3IgPSAiY2VudGVyIiwgYWxpZ24gPSAiY2VudGVyIiwgeCA9IDAuNSwgeSA9IDEsIHNob3dhcnJvdyA9IEZBTFNFKSkNCg0Kc2NhbGVDb2wgPC0gc2NhbGVDb2wgJT4lIHJvd2lkX3RvX2NvbHVtbigpICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoZmxleGlibGUscmlnaWQscHJvZHVjdCkpDQpzY2F0dGVyQ29sIDwtIGdncGxvdChzY2FsZUNvbCwgYWVzKHggPSB2YWx1ZSwgeSA9IHJvd2lkLCBjb2xvdXIgPSBuYW1lKSkgKw0KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjc1KSArICMgcG9pbnQgZ2VvbSBpcyB1c2VkIHRvIGNyZWF0ZSBzY2F0dGVycGxvdHMNCiAgICB0aGVtZV9taW5pbWFsKCkgDQpzY2F0dGVyQ29sIDwtIGdncGxvdGx5KHNjYXR0ZXJDb2wpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAic2NhbGVDb2wiLCBmb250ID0gZiwgeHJlZiA9ICJwYXBlciIsIHlyZWYgPSAicGFwZXIiLCB5YW5jaG9yID0gImJvdHRvbSIsIHhhbmNob3IgPSAiY2VudGVyIiwgYWxpZ24gPSAiY2VudGVyIiwgeCA9IDAuNSwgeSA9IDEsIHNob3dhcnJvdyA9IEZBTFNFKSkNCg0KZGZfaWQgPC0gZGYgJT4lIHJvd2lkX3RvX2NvbHVtbigpICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoZmxleGlibGUscmlnaWQscHJvZHVjdCkpDQpzY2F0dGVyRGYgPC0gZ2dwbG90KGRmX2lkLCBhZXMoeCA9IHZhbHVlLCB5ID0gcm93aWQsIGNvbG91ciA9IG5hbWUpKSArDQogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsgIyBwb2ludCBnZW9tIGlzIHVzZWQgdG8gY3JlYXRlIHNjYXR0ZXJwbG90cw0KICAgIHRoZW1lX21pbmltYWwoKSANCnNjYXR0ZXJEZiA8LSBnZ3Bsb3RseShzY2F0dGVyRGYpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAibm9ybWFsIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCnN1YnBsb3Qoc2NhdHRlclJvdywgc2NhdHRlckNvbCwgc2NhdHRlckRmKSAlPiUgbGF5b3V0KCkNCmBgYA0KDQo=